<!DOCTYPE html>


<html lang="en">
  

    <head>
      <meta charset="utf-8" />
        
      <meta name="description" content="这是一个简单地博客网站" />
      
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1"
      />
      <title>第一篇文章 |  李三水的学习笔记</title>
  <meta name="generator" content="hexo-theme-ayer">
      
      <link rel="shortcut icon" href="/favicon.ico" />
       
<link rel="stylesheet" href="/dist/main.css">

      
<link rel="stylesheet" href="/css/fonts/remixicon.css">

      
<link rel="stylesheet" href="/css/custom.css">
 
      <script src="https://cdn.staticfile.org/pace/1.2.4/pace.min.js"></script>
       
 

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bulma@5.0.1/bulma.min.css"
      />
      <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.19/dist/sweetalert2.min.js"></script>

      <!-- mermaid -->
      
      <style>
        .swal2-styled.swal2-confirm {
          font-size: 1.6rem;
        }
      </style>
    </head>
  </html>
</html>


<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-第一篇文章"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  第一篇文章
</h1>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2023/03/30/%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/" class="article-date">
  <time datetime="2023-03-30T11:47:56.000Z" itemprop="datePublished">2023-03-30</time>
</a>   
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> Word count:</span>
            <span class="post-count">36k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> Reading time≈</span>
            <span class="post-count">154 min</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="C-11新特性"><a href="#C-11新特性" class="headerlink" title="C++11新特性"></a>C++11新特性</h2><p>参考链接：<a target="_blank" rel="noopener" href="https://subingwen.cn/cplusplus/">https://subingwen.cn/cplusplus/</a></p>
<h3 id="1-原始字面量"><a href="#1-原始字面量" class="headerlink" title="1. 原始字面量"></a>1. 原始字面量</h3><p>定义方式：R”(原样的字符串)”</p>
<p>在 C++11 中添加了定义原始字符串的字面量，定义方式为：R “xxx(原始字符串)xxx” 其中（）两边的字符串可以省略。原始字面量 R 可以直接表示字符串的实际含义，而不需要额外对字符串做转义或连接等操作。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">string str1 = <span class="string">R&quot;(D:\hello\world\test.text)&quot;</span>;</span><br></pre></td></tr></table></figure>

<p>最后强调一个细节：在R “xxx(raw string)xxx” 中，原始字符串必须用括号（）括起来，括号的前后可以加其他字符串，所加的字符串会被忽略，并且加的字符串必须在括号两边同时出现。</p>
<h3 id="2-指针空值类型-nullptr"><a href="#2-指针空值类型-nullptr" class="headerlink" title="2. 指针空值类型-nullptr"></a>2. 指针空值类型-nullptr</h3><p>在 C++ 程序开发中，为了提高程序的健壮性，一般会在定义指针的同时完成初始化操作，或者在指针的指向尚未明确的情况下，都会给指针初始化为 NULL，避免产生野指针（没有明确指向的指针，操作也这种指针极可能导致程序发生异常）。C++98&#x2F;03 标准中，将一个指针初始化为空指针的方式有 2 种：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> *ptr = <span class="number">0</span>;</span><br><span class="line"><span class="type">char</span> *ptr = <span class="literal">NULL</span>;</span><br></pre></td></tr></table></figure>

<p>C++ 中将 NULL 定义为字面常量 0，并不能保证在所有场景下都能很好的工作，比如，函数重载时，NULL 和 0 无法区分。出于兼容性的考虑，C++11 标准并没有对 NULL 的宏定义做任何修改，而是引入了一个新的关键字 nullptr。nullptr 专用于初始化空类型指针，不同类型的指针变量都可以使用 nullptr 来初始化：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>*    ptr1 = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="type">char</span>*   ptr2 = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="type">double</span>* ptr3 = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="comment">//对应上面的代码编译器会分别将 nullptr 隐式转换成 int*、char* 以及 double* 指针类型。</span></span><br></pre></td></tr></table></figure>

<p><strong>注：nullptr 无法隐式转换为整形，但是可以隐式匹配指针类型。在 C++11 标准下，相比 NULL 和 0，使用 nullptr 初始化空指针可以令我们编写的程序更加健壮。</strong></p>
<h3 id="A-数值类型和字符串之间的转换"><a href="#A-数值类型和字符串之间的转换" class="headerlink" title="A. 数值类型和字符串之间的转换"></a>A. 数值类型和字符串之间的转换</h3><h4 id="1-1-数值转换为字符串"><a href="#1-1-数值转换为字符串" class="headerlink" title="1.1 数值转换为字符串"></a>1.1 数值转换为字符串</h4><p>使用 to_string() 方法可以非常方便地将各种数值类型转换为字符串类型，这是一个重载函，函数声明位于头文件 <string> 中，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 头文件 &lt;string&gt;</span></span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">int</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">long</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">long</span> <span class="type">long</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">unsigned</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">unsigned</span> <span class="type">long</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">float</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">double</span> val)</span></span>;</span><br><span class="line"><span class="function">string <span class="title">to_string</span> <span class="params">(<span class="type">long</span> <span class="type">double</span> val)</span></span>;</span><br><span class="line"></span><br><span class="line">使用示例：</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    string pi = <span class="string">&quot;pi is &quot;</span> + <span class="built_in">to_string</span>(<span class="number">3.1415926</span>);</span><br><span class="line">    string love = <span class="string">&quot;love is &quot;</span> + <span class="built_in">to_string</span>(<span class="number">5.20</span> + <span class="number">13.14</span>);</span><br><span class="line">    cout &lt;&lt; pi &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; love &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="1-2-字符串转换为数值"><a href="#1-2-字符串转换为数值" class="headerlink" title="1.2 字符串转换为数值"></a>1.2 字符串转换为数值</h4><p>由于 C++ 中的数值类型包括整形和浮点型，因此针对于不同的类型提供了不同的函数，通过调用这些函数可以将字符串类型转换为对应的数值类型。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;string&gt;</span></span><br><span class="line"><span class="function"><span class="type">int</span>       <span class="title">stoi</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span>, <span class="type">int</span> base = <span class="number">10</span> )</span></span>;</span><br><span class="line"><span class="function"><span class="type">long</span>      <span class="title">stol</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span>, <span class="type">int</span> base = <span class="number">10</span> )</span></span>;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">stoll</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span>, <span class="type">int</span> base = <span class="number">10</span> )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">unsigned</span> <span class="type">long</span>      <span class="title">stoul</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span>, <span class="type">int</span> base = <span class="number">10</span> )</span></span>;</span><br><span class="line"><span class="function"><span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> <span class="title">stoull</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span>, <span class="type">int</span> base = <span class="number">10</span> )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">float</span>       <span class="title">stof</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span> )</span></span>;</span><br><span class="line"><span class="function"><span class="type">double</span>      <span class="title">stod</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span> )</span></span>;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">double</span> <span class="title">stold</span><span class="params">( <span class="type">const</span> std::string&amp; str, std::<span class="type">size_t</span>* pos = <span class="number">0</span> )</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">str：要转换的字符串</span></span><br><span class="line"><span class="comment">pos：传出参数，记录从哪个字符开始无法继续进行解析，比如: 123abc, 传出的位置为 3</span></span><br><span class="line"><span class="comment">base：若 base 为 0 ，则自动检测数值进制：若前缀为 0 ，则为八进制，若前缀为 0x 或 0X，则为十六进制，否则为十进制。</span></span><br></pre></td></tr></table></figure>

<p>示例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    string str1 = <span class="string">&quot;45&quot;</span>;</span><br><span class="line">    string str2 = <span class="string">&quot;3.14159&quot;</span>;</span><br><span class="line">    string str3 = <span class="string">&quot;9527 with words&quot;</span>;</span><br><span class="line">    string str4 = <span class="string">&quot;words and 2&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> myint1 = std::<span class="built_in">stoi</span>(str1);</span><br><span class="line">    <span class="type">float</span> myint2 = std::<span class="built_in">stof</span>(str2);</span><br><span class="line">    <span class="type">int</span> myint3 = std::<span class="built_in">stoi</span>(str3);</span><br><span class="line">    <span class="comment">// 错误： &#x27;std::invalid_argument&#x27;</span></span><br><span class="line">    <span class="comment">// int myint4 = std::stoi(str4);</span></span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;std::stoi(\&quot;&quot;</span> &lt;&lt; str1 &lt;&lt; <span class="string">&quot;\&quot;) is &quot;</span> &lt;&lt; myint1 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;std::stof(\&quot;&quot;</span> &lt;&lt; str2 &lt;&lt; <span class="string">&quot;\&quot;) is &quot;</span> &lt;&lt; myint2 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;std::stoi(\&quot;&quot;</span> &lt;&lt; str3 &lt;&lt; <span class="string">&quot;\&quot;) is &quot;</span> &lt;&lt; myint3 &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// cout &lt;&lt; &quot;std::stoi(\&quot;&quot; &lt;&lt; str4 &lt;&lt; &quot;\&quot;) is &quot; &lt;&lt; myint4 &lt;&lt; endl; //转换失败</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*输出：</span></span><br><span class="line"><span class="comment">std::stoi(&quot;45&quot;) is 45</span></span><br><span class="line"><span class="comment">std::stof(&quot;3.14159&quot;) is 3.14159</span></span><br><span class="line"><span class="comment">std::stoi(&quot;9527 with words&quot;) is 9527</span></span><br></pre></td></tr></table></figure>

<p><strong>结论：</strong></p>
<ul>
<li><strong>如果字符串中所有字符都是数值类型，整个字符串会被转换为对应的数值，并通过返回值返回</strong></li>
<li><strong>如果字符串的前半部分字符是数值类型，后半部不是，那么前半部分会被转换为对应的数值，并通过返回值返回</strong></li>
<li><strong>如果字符第一个字符不是数值类型转换失败</strong></li>
</ul>
<h4 id="1-3-noexcept"><a href="#1-3-noexcept" class="headerlink" title="1.3 noexcept"></a>1.3 noexcept</h4><p>noexcept 形如其名， 表示其修饰的函数不会抛出异常 。不过与 <code>throw ()</code> 动态异常声明不同的是，<code>在 C++11 中如果 noexcept 修饰的函数抛出了异常，编译器可以选择直接调用 std::terminate () 函数来终止程序的运行，这比基于异常机制的 throw () 在效率上会高一些</code>。这是因为异常机制会带来一些额外开销，比如函数抛出异常，会导致函数栈被依次地展开（栈解旋），并自动调用析构函数释放栈上的所有对象。<br>因此对于不会抛出异常的函数我们可以这样写:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">double</span> <span class="title">divisionMethod</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> <span class="keyword">noexcept</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (b == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;division by zero!!!&quot;</span> &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> a / b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>从语法上讲，noexcept 修饰符有两种形式：</p>
<ul>
<li><p>简单地在函数声明后加上 noexcept 关键字</p>
</li>
<li><p>可以接受一个常量表达式作为参数，如下所示∶</p>
  <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">double</span> <span class="title">divisionMethod</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> <span class="title">noexcept</span><span class="params">(常量表达式)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">常量表达式的结果会被转换成一个 bool 类型的值：</span></span><br><span class="line"><span class="comment">值为 true，表示函数不会抛出异常</span></span><br><span class="line"><span class="comment">值为 false，表示有可能抛出异常这里</span></span><br><span class="line"><span class="comment">不带常量表达式的 noexcept 相当于声明了 noexcept（true），即不会抛出异常。</span></span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="B-x3D-default和-x3D-delete"><a href="#B-x3D-default和-x3D-delete" class="headerlink" title="B. &#x3D;default和&#x3D;delete"></a>B. &#x3D;default和&#x3D;delete</h3><p>在 C++11 标准中称 &#x3D; default 修饰的函数为显式默认【缺省】（explicit defaulted）函数，而称 &#x3D;delete 修饰的函数为删除（deleted）函数或者显示删除函数。</p>
<p> C++11 引入显式默认和显式删除是为了增强对类默认函数的控制，让程序员能够更加精细地控制默认版本的函数。</p>
<h4 id="1-1-x3D-default"><a href="#1-1-x3D-default" class="headerlink" title="1.1 &#x3D;default"></a>1.1 &#x3D;default</h4><p>我们可以在类内部修饰满足条件的类函数为显示默认函数，也可以在类定义之外修饰成员函数为默认函数。下面举例说明：</p>
<p>1.我们可以在定义类的时候直接在类内部指定默认函数，如下所示：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Base</span>() = <span class="keyword">default</span>;</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">const</span> Base&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">    <span class="built_in">Base</span>(Base&amp;&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">    Base&amp; <span class="keyword">operator</span>= (<span class="type">const</span> Base&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">    Base&amp; <span class="keyword">operator</span>= (Base&amp;&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">    ~<span class="built_in">Base</span>() = <span class="keyword">default</span>;</span><br><span class="line">&#125;;<span class="comment">//使用 =defaut 指定的默认函数和类提供的默认函数是等价的</span></span><br></pre></td></tr></table></figure>

<p>2.默认函数除了在类定义的内部指定，也可以在类的外部指定，如下所示：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 类定义</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Base</span>();</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">const</span> Base&amp; obj);</span><br><span class="line">    <span class="built_in">Base</span>(Base&amp;&amp; obj);</span><br><span class="line">    Base&amp; <span class="keyword">operator</span>= (<span class="type">const</span> Base&amp; obj);</span><br><span class="line">    Base&amp; <span class="keyword">operator</span>= (Base&amp;&amp; obj);</span><br><span class="line">    ~<span class="built_in">Base</span>();</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 在类定义之外指定成员函数为默认函数</span></span><br><span class="line">Base::<span class="built_in">Base</span>() = <span class="keyword">default</span>;</span><br><span class="line">Base::<span class="built_in">Base</span>(<span class="type">const</span> Base&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">Base::<span class="built_in">Base</span>(Base&amp;&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">Base&amp; Base::<span class="keyword">operator</span>= (<span class="type">const</span> Base&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">Base&amp; Base::<span class="keyword">operator</span>= (Base&amp;&amp; obj) = <span class="keyword">default</span>;</span><br><span class="line">Base::~<span class="built_in">Base</span>() = <span class="keyword">default</span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">**如果程序猿对 C++ 类提供的默认函数（上面提到的六个函数）进行了实现，那么可以通过 =default 将他们再次指定为默认函数，不能使用 =default 修饰这六个函数以外的函数。**</span></span><br></pre></td></tr></table></figure>

<h4 id="1-2-x3D-delete"><a href="#1-2-x3D-delete" class="headerlink" title="1.2 &#x3D;delete"></a>1.2 &#x3D;delete</h4><p>&#x3D;delete 表示显示删除，<strong>显式删除可以避免用户使用一些不应该使用的类的成员函数</strong>，使用这种方式可以有效的防止某些类型之间自动进行隐式类型转换产生的错误。下面举例说明：</p>
<p>1.禁止使用默认生成的函数:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Base</span>() = <span class="keyword">default</span>;</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">const</span> Base&amp; obj) = <span class="keyword">delete</span>;  <span class="comment">//禁用拷贝构造函数</span></span><br><span class="line">    Base&amp; <span class="keyword">operator</span>= (<span class="type">const</span> Base&amp; obj) = <span class="keyword">delete</span>; <span class="comment">//禁用 = 进行对象复制</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Base b;</span><br><span class="line">    <span class="function">Base <span class="title">tmp1</span><span class="params">(b)</span></span>;    <span class="comment">// error  拷贝构造函数已被显示删除，无法拷贝对象</span></span><br><span class="line">    Base tmp = b;    <span class="comment">// error  复制对象的赋值操作符重载函数已被显示删除，无法复制对象</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2.禁止使用自定义函数</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">int</span> num) : <span class="built_in">m_num</span>(num) &#123;&#125;</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">char</span> c) = <span class="keyword">delete</span>;             <span class="comment">//禁用带 char 类型参数的构造函数，防止隐式类型转换（char 转 int)</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">(<span class="type">char</span> c)</span> </span>= <span class="keyword">delete</span>;      <span class="comment">//禁止使用带 char 类型的自定义函数，防止隐式类型转换（char 转 int)</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;num: &quot;</span> &lt;&lt; m_num &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">(<span class="type">int</span> num)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;num: &quot;</span> &lt;&lt; num &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> m_num;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">Base <span class="title">b</span><span class="params">(<span class="number">97</span>)</span></span>;       <span class="comment">// &#x27;a&#x27; 对应的 acscii 值为97</span></span><br><span class="line">    <span class="function">Base <span class="title">b1</span><span class="params">(<span class="string">&#x27;a&#x27;</span>)</span></span>;     <span class="comment">// error 对应的构造函数被禁用，因此无法使用该构造函数构造对象</span></span><br><span class="line">    b.<span class="built_in">print</span>();</span><br><span class="line">    b.<span class="built_in">print</span>(<span class="number">97</span>);</span><br><span class="line">    b.<span class="built_in">print</span>(<span class="string">&#x27;a&#x27;</span>);     <span class="comment">// error 对应的打印函数被禁用，因此无法给函数传递 char 类型参数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="3-1-constexpr-修饰常量表达式"><a href="#3-1-constexpr-修饰常量表达式" class="headerlink" title="3.1 constexpr-修饰常量表达式"></a>3.1 constexpr-修饰常量表达式</h3><ol>
<li><p>在 C++11 中添加了一个新的关键字 constexpr，这个关键字是用来修饰常量表达式的。所谓常量表达式，指的就是由多个（≥1）常量（值不会改变）组成并且在编译过程中就得到计算结果的表达式。</p>
</li>
<li><p>需要额外强调的是，常量表达式和非常量表达式的计算时机不同，非常量表达式只能在程序运行阶段计算出结果，但是常量表达式的计算往往发生在程序的编译阶段，这可以极大提高程序的执行效率，因为表达式只需要在编译阶段计算一次，节省了每次程序运行时都需要计算一次的时间。</p>
</li>
<li><p>那么问题来了，编译器如何识别表达式是不是常量表达式呢？在 C++11 中添加了 constexpr 关键字之后就可以在程序中使用它来修改常量表达式，用来提高程序的执行效率。<strong>在使用中建议将 const 和 constexpr 的功能区分开，即凡是表达“只读”语义的场景都使用 const，表达“常量”语义的场景都使用 constexpr。</strong></p>
</li>
<li><p>在定义常量时，const 和 constexpr 是等价的，都可以在程序的编译阶段计算出结果，例如：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> m = <span class="built_in">f</span>();  <span class="comment">// 不是常量表达式，m的值只有在运行时才会获取。</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> i=<span class="number">520</span>;    <span class="comment">// 是一个常量表达式</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> j=i+<span class="number">1</span>;    <span class="comment">// 是一个常量表达式</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">constexpr</span> <span class="type">int</span> i=<span class="number">520</span>;    <span class="comment">// 是一个常量表达式</span></span><br><span class="line"><span class="keyword">constexpr</span> <span class="type">int</span> j=i+<span class="number">1</span>;    <span class="comment">// 是一个常量表达式</span></span><br></pre></td></tr></table></figure></li>
</ol>
<h3 id="3-2-常量表达式函数"><a href="#3-2-常量表达式函数" class="headerlink" title="3.2 常量表达式函数"></a>3.2 常量表达式函数</h3><h4 id="3-2-1-修饰函数"><a href="#3-2-1-修饰函数" class="headerlink" title="3.2.1 修饰函数"></a>3.2.1 修饰函数</h4><p>1.函数必须要有返回值，并且 return 返回的表达式必须是常量表达式。</p>
<p>2.函数在使用之前，必须有对应的定义语句。</p>
<p>3.整个函数的函数体中，不能出现非常量表达式之外的语句（using 指令、typedef 语句以及 static_assert 断言、return 语句除外）。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// error 错误写法</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="type">int</span> <span class="title">func1</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">constexpr</span> <span class="type">int</span> a = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">constexpr</span> <span class="type">int</span> b = <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; b; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;i: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ok</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="type">int</span> <span class="title">func2</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">using</span> mytype = <span class="type">int</span>;</span><br><span class="line">    <span class="keyword">constexpr</span> mytype a = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">constexpr</span> mytype b = <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">constexpr</span> mytype c = a * b;</span><br><span class="line">    <span class="keyword">return</span> c - (a + b);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上三条规则不仅对应普通函数适用，对应类的成员函数也是适用的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">constexpr</span> <span class="type">int</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">constexpr</span> <span class="type">int</span> var = <span class="number">100</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span> * var;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    <span class="keyword">constexpr</span> <span class="type">int</span> num = t.<span class="built_in">func</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;num: &quot;</span> &lt;&lt; num &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="3-2-2-修饰模板函数"><a href="#3-2-2-修饰模板函数" class="headerlink" title="3.2.2 修饰模板函数"></a>3.2.2 修饰模板函数</h4><p>C++11 语法中，constexpr 可以修饰函数模板，但由于模板中类型的不确定性，因此函数模板实例化后的模板函数是否符合常量表达式函数的要求也是不确定的。如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求，则 constexpr 会被自动忽略，即该函数就等同于一个普通函数。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span>* name;</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> T <span class="title">dispaly</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Person</span> p &#123; <span class="string">&quot;luffy&quot;</span>, <span class="number">19</span> &#125;;</span><br><span class="line">    <span class="comment">//普通函数</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Person</span> ret = <span class="built_in">dispaly</span>(p);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;luffy&#x27;s name: &quot;</span> &lt;&lt; ret.name &lt;&lt; <span class="string">&quot;, age: &quot;</span> &lt;&lt; ret.age &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//常量表达式函数</span></span><br><span class="line">    <span class="keyword">constexpr</span> <span class="type">int</span> ret1 = <span class="built_in">dispaly</span>(<span class="number">250</span>);</span><br><span class="line">    cout &lt;&lt; ret1 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constexpr</span> <span class="keyword">struct</span> <span class="title class_">Person</span> p1 &#123; <span class="string">&quot;luffy&quot;</span>, <span class="number">19</span> &#125;;</span><br><span class="line">    <span class="keyword">constexpr</span> <span class="keyword">struct</span> <span class="title class_">Person</span> p2 = <span class="built_in">dispaly</span>(p1);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;luffy&#x27;s name: &quot;</span> &lt;&lt; p2.name &lt;&lt; <span class="string">&quot;, age: &quot;</span> &lt;&lt; p2.age &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="3-2-3-修饰构造函数"><a href="#3-2-3-修饰构造函数" class="headerlink" title="3.2.3 修饰构造函数"></a>3.2.3 修饰构造函数</h4><p>如果想用直接得到一个常量对象，也可以使用 constexpr 修饰一个构造函数，这样就可以得到一个常量构造函数了。<strong>常量构造函数有一个要求：构造函数的函数体必须为空，并且必须采用初始化列表的方式为各个成员赋值。</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="comment">////初始化列表属性</span></span><br><span class="line">    <span class="function"><span class="keyword">constexpr</span> <span class="title">Person</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* p, <span class="type">int</span> age)</span> :name(p), age(age)</span></span><br><span class="line"><span class="function">    &#123;</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span>* name;</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">constexpr</span> <span class="keyword">struct</span> <span class="title class_">Person</span> <span class="built_in">p1</span>(<span class="string">&quot;luffy&quot;</span>, <span class="number">19</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;luffy&#x27;s name: &quot;</span> &lt;&lt; p1.name &lt;&lt; <span class="string">&quot;, age: &quot;</span> &lt;&lt; p1.age &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="4-自动类型推导"><a href="#4-自动类型推导" class="headerlink" title="4. 自动类型推导"></a>4. 自动类型推导</h3><h4 id="4-1-auto"><a href="#4-1-auto" class="headerlink" title="4.1 auto"></a>4.1 auto</h4><h5 id="4-1-1-推导规则"><a href="#4-1-1-推导规则" class="headerlink" title="4.1.1 推导规则"></a>4.1.1 推导规则</h5><p>C++11 中 auto 并不代表一种实际的数据类型，只是一个类型声明的 “占位符”，auto 并不是万能的在任意场景下都能够推导出变量的实际类型，<strong>使用auto声明的变量必须要进行初始化</strong>，以让编译器推导出它的实际类型，在编译时将auto占位符替换为真正的类型。使用语法如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">auto</span> 变量名 = 变量值;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> x = <span class="number">3.14</span>;      <span class="comment">// x 是浮点型 double</span></span><br><span class="line"><span class="keyword">auto</span> y = <span class="number">520</span>;       <span class="comment">// y 是整形 int</span></span><br><span class="line"><span class="keyword">auto</span> z = <span class="string">&#x27;a&#x27;</span>;       <span class="comment">// z 是字符型 char</span></span><br><span class="line"><span class="keyword">auto</span> nb;            <span class="comment">// error，变量必须要初始化</span></span><br><span class="line"><span class="keyword">auto</span> <span class="type">double</span> nbl;    <span class="comment">// 语法错误, 不能修改数据类型   </span></span><br></pre></td></tr></table></figure>

<p>不仅如此，auto 还可以和指针、引用结合起来使用也可以带上 const、volatile 限定符，在不同的场景下有对应的推导规则，规则内容如下：</p>
<ul>
<li>当变量不是指针或者引用类型时，推导的结果中不会保留 const、volatile 关键字</li>
<li>当变量是指针或者引用类型时，推导的结果中会保留 const、volatile 关键字</li>
</ul>
<h5 id="4-1-2-auto的限制"><a href="#4-1-2-auto的限制" class="headerlink" title="4.1.2 auto的限制"></a>4.1.2 auto的限制</h5><p>1.不能作为函数参数使用。因为只有在函数调用的时候才会给函数参数传递实参，auto 要求必须要给修饰的变量赋值，因此二者矛盾。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">func</span><span class="params">(<span class="keyword">auto</span> a, <span class="keyword">auto</span> b)</span>	<span class="comment">// error</span></span></span><br><span class="line"><span class="function"></span>&#123;	</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;a: &quot;</span> &lt;&lt; a &lt;&lt;<span class="string">&quot;, b: &quot;</span> &lt;&lt; b &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2.不能用于类的非静态成员变量的初始化</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">auto</span> v1 = <span class="number">0</span>;                    <span class="comment">// error</span></span><br><span class="line">    <span class="type">static</span> <span class="keyword">auto</span> v2 = <span class="number">0</span>;             <span class="comment">// error,类的静态非常量成员不允许在类内部直接初始化</span></span><br><span class="line">    <span class="type">static</span> <span class="type">const</span> <span class="keyword">auto</span> v3 = <span class="number">10</span>;      <span class="comment">// ok</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>3.不能使用 auto 关键字定义数组</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> array[] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>&#125;;  <span class="comment">// 定义数组</span></span><br><span class="line">    <span class="keyword">auto</span> t1 = array;            <span class="comment">// ok, t1被推导为 int* 类型</span></span><br><span class="line">    <span class="keyword">auto</span> t2[] = array;          <span class="comment">// error, auto无法定义数组</span></span><br><span class="line">    <span class="keyword">auto</span> t3[] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>&#125;;;   <span class="comment">// error, auto无法定义数组</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>4.无法使用 auto 推导出模板参数</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test&lt;<span class="type">double</span>&gt; t;</span><br><span class="line">    Test&lt;<span class="keyword">auto</span>&gt; t1 = t;           <span class="comment">// error, 无法推导出模板类型</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="4-1-3-auto-的应用"><a href="#4-1-3-auto-的应用" class="headerlink" title="4.1.3 auto 的应用"></a>4.1.3 auto 的应用</h5><p>1.用于STL的容器遍历。</p>
<p>2.用于泛型编程，在使用模板的时候，很多情况下我们不知道变量应该定义为什么类型，比如下面的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">T1</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">get</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">10</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">T2</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> string <span class="title">get</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello, world&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">A</span>&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> val = A::<span class="built_in">get</span>();<span class="comment">//起占位符的作用</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;val: &quot;</span> &lt;&lt; val &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">func</span>&lt;T1&gt;();</span><br><span class="line">    <span class="built_in">func</span>&lt;T2&gt;();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="4-2-decltype"><a href="#4-2-decltype" class="headerlink" title="4.2 decltype"></a>4.2 decltype</h4><p>在某些情况下，不需要或者不能定义变量，但是希望得到某种类型，这时候就可以使用 C++11 提供的 decltype 关键字了，它的作用是在编译器编译的时候推导出一个表达式的类型，语法格式如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">decltype</span> (表达式)</span><br></pre></td></tr></table></figure>

<p>decltype 是 “declare type” 的缩写，意思是 “声明类型”。decltype 的推导是在编译期完成的，它只是用于表达式类型的推导，并不会计算表达式的值。来看一组简单的例子:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">decltype</span>(a) b = <span class="number">99</span>;                 <span class="comment">// b -&gt; int</span></span><br><span class="line"><span class="keyword">decltype</span>(a+<span class="number">3.14</span>) c = <span class="number">52.13</span>;         <span class="comment">// c -&gt; double</span></span><br><span class="line"><span class="keyword">decltype</span>(a+b*c) d = <span class="number">520.1314</span>;       <span class="comment">// d -&gt; double</span></span><br></pre></td></tr></table></figure>

<h5 id="4-2-1-推导规则"><a href="#4-2-1-推导规则" class="headerlink" title="4.2.1 推导规则"></a>4.2.1 推导规则</h5><ul>
<li><p>表达式为普通变量或者普通表达式或者类表达式，在这种情况下，使用 decltype 推导出的类型和表达式的类型是一致的。</p>
</li>
<li><p>表达式是函数调用，使用 decltype 推导出的类型和函数返回值一致。</p>
</li>
<li><p>表达式是一个左值，或者被括号 ( ) 包围，使用 decltype 推导出的是表达式类型的引用（如果有 const、volatile 限定符不能忽略）。</p>
</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">int</span> num;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">const</span> Test obj;</span><br><span class="line">    <span class="comment">//带有括号的表达式</span></span><br><span class="line">    <span class="keyword">decltype</span>(obj.num) a = <span class="number">0</span>;<span class="comment">//a: int类型</span></span><br><span class="line">    <span class="keyword">decltype</span>((obj.num)) b = a; <span class="comment">//a: const int&amp;类型</span></span><br><span class="line">    <span class="comment">//加法表达式</span></span><br><span class="line">    <span class="type">int</span> n = <span class="number">0</span>, m = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">decltype</span>(n + m) c = <span class="number">0</span>;<span class="comment">//c:int</span></span><br><span class="line">    <span class="keyword">decltype</span>(n = n + m) d = n;<span class="comment">//表达式是左值，d:int&amp;类型</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="4-2-2-decltype的应用"><a href="#4-2-2-decltype的应用" class="headerlink" title="4.2.2 decltype的应用"></a>4.2.2 decltype的应用</h5><p>关于 decltype 的应用多出现在泛型编程中。比如我们编写一个类模板，在里边添加遍历容器的函数，操作如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Container</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(T&amp; c)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (m_it = c.<span class="built_in">begin</span>(); m_it != c.<span class="built_in">end</span>(); ++m_it)</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; *m_it &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        cout &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">decltype</span>(<span class="built_in">T</span>().<span class="built_in">begin</span>()) m_it;  <span class="comment">// 这里m_it不能确定迭代器类型,decltype 就可以完美的解决这个问题了</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">const</span> list&lt;<span class="type">int</span>&gt; lst&#123; <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span> &#125;;</span><br><span class="line">    Container&lt;<span class="type">const</span> list&lt;<span class="type">int</span>&gt;&gt; obj;</span><br><span class="line">    obj.<span class="built_in">func</span>(lst);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="4-3-返回类型后置"><a href="#4-3-返回类型后置" class="headerlink" title="4.3 返回类型后置"></a>4.3 返回类型后置</h4><p>在C++11中增加了返回类型后置语法，说明白一点就是将decltype和auto结合起来完成返回类型的推导。其语法格式如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 符号 -&gt; 后边跟随的是函数返回值的类型</span></span><br><span class="line"><span class="function"><span class="keyword">auto</span> <span class="title">func</span><span class="params">(参数<span class="number">1</span>, 参数<span class="number">2</span>, ...)</span> -&gt; <span class="title">decltype</span><span class="params">(参数表达式)</span></span></span><br></pre></td></tr></table></figure>

<p>举例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//错误写法：decltype 中的 t 和 u 都是函数参数，直接这样写相当于变量还没有定义就直接用上了，这时候变量还不存在</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="keyword">decltype</span>(t+u) <span class="built_in">add</span>(T t, U u)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> t + u;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//正确写法，使用返回类型后置</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="comment">// 返回类型后置语法</span></span><br><span class="line"><span class="function"><span class="keyword">auto</span> <span class="title">add</span><span class="params">(T t, U u)</span> -&gt; <span class="title">decltype</span><span class="params">(t+u)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> t + u;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> x = <span class="number">520</span>;</span><br><span class="line">    <span class="type">double</span> y = <span class="number">13.14</span>;</span><br><span class="line">    <span class="comment">// auto z = add&lt;int, double&gt;(x, y);</span></span><br><span class="line">    <span class="keyword">auto</span> z = <span class="built_in">add</span>(x, y);		<span class="comment">// 简化之后的写法</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;z: &quot;</span> &lt;&lt; z &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="5-final和override"><a href="#5-final和override" class="headerlink" title="5. final和override"></a>5. final和override</h3><h4 id="5-1-final"><a href="#5-1-final" class="headerlink" title="5.1 final"></a>5.1 final</h4><p>C++ 中增加了 final 关键字来限制某个类不能被继承，或者某个虚函数不能被重写，和 Java 的 final 关键字的功能是类似的。如果使用 final 修饰函数，只能修饰虚函数，<strong>并且要把final关键字放到类或者函数的后面。</strong></p>
<h5 id="5-1-1-修饰函数"><a href="#5-1-1-修饰函数" class="headerlink" title="5.1.1 修饰函数"></a>5.1.1 修饰函数</h5><p>如果使用 final 修饰函数，只能修饰虚函数，这样就能阻止子类重写父类的这个函数了：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Base class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Child</span> : <span class="keyword">public</span> Base</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">final</span>         <span class="comment">//使用 final 修饰函数</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Child class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GrandChild</span> : <span class="keyword">public</span> Child</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 语法错误, 不允许重写</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;GrandChild class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">test() 是基类中的一个虚函数，在子类中重写了这个方法，但是不希望孙子类中继续重写这个方法了，因此在子类中将 test() 方法标记为 final，孙子类中对这个方法就只有使用的份了。</span></span><br></pre></td></tr></table></figure>

<h5 id="5-1-2-修饰类"><a href="#5-1-2-修饰类" class="headerlink" title="5.1.2 修饰类"></a>5.1.2 修饰类</h5><p>使用 final 关键字修饰过的类是不允许被继承的，也就是说这个类不能有派生类。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Base class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Child</span> <span class="keyword">final</span>: <span class="keyword">public</span> Base  <span class="comment">//使用 final 关键字修饰类</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Child class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// error, 语法错误,不允许继承</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GrandChild</span> : <span class="keyword">public</span> Child</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h4 id="5-2-override"><a href="#5-2-override" class="headerlink" title="5.2 override"></a>5.2 override</h4><p>override 关键字<strong>确保在派生类中声明的重写函数与基类的虚函数有相同的签名，</strong>同时也明确表明将会重写基类的虚函数，这样就可以保证重写的虚函数的正确性，也提高了代码的可读性，和 final 一样这个关键字要写到方法的后面。使用方法如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Base class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Child</span> : <span class="keyword">public</span> Base</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">override</span>   <span class="comment">//保证重写的虚函数的正确性，也提高了代码的可读性,主要用于检查重写函数写对没有</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Child class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GrandChild</span> : <span class="keyword">public</span> Child</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">override</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Child class...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="6-模板的优化"><a href="#6-模板的优化" class="headerlink" title="6. 模板的优化"></a>6. 模板的优化</h3><h4 id="6-1-模板的右尖括号"><a href="#6-1-模板的右尖括号" class="headerlink" title="6.1 模板的右尖括号"></a>6.1 模板的右尖括号</h4><p>在泛型编程中，模板实例化有一个非常繁琐的地方，那就是连续的两个右尖括号（&gt;&gt;）会被编译器解析成右移操作符，而不是模板参数表的结束。</p>
<p>C++11改进了编译器的解析规则，尽可能地将多个右尖括号（&gt;）解析成模板参数结束符，方便我们编写模板相关的代码。</p>
<h4 id="6-2-默认模板参数"><a href="#6-2-默认模板参数" class="headerlink" title="6.2 默认模板参数"></a>6.2 默认模板参数</h4><p>在 C++98&#x2F;03 标准中，类模板可以有默认的模板参数,但是不支持函数的默认模板参数，在C++11中添加了对函数模板默认参数的支持:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T=<span class="type">int</span>&gt;	<span class="comment">// C++98/03不支持这种写法, C++11中支持这种写法</span></span><br><span class="line"><span class="type">void</span> <span class="built_in">func</span>(T t)</span><br><span class="line">&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;current value: &quot;</span> &lt;&lt; t &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">func</span>(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结论：当所有模板参数都有默认参数时，函数模板的调用如同一个普通函数。但对于类模板而言，哪怕所有参数都有默认参数，在使用时也必须在模板名后跟随 &lt;&gt; 来实例化。</p>
<p>另外：函数模板的默认模板参数在使用规则上和其他的默认参数也有一些不同，它没有必须写在参数表最后的限制.</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> R = <span class="type">int</span>, <span class="keyword">typename</span> N&gt;</span><br><span class="line">R <span class="built_in">func</span>(N arg)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> arg;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> ret1 = <span class="built_in">func</span>(<span class="number">520</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;return value-1: &quot;</span> &lt;&lt; ret1 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> ret2 = <span class="built_in">func</span>&lt;<span class="type">double</span>&gt;(<span class="number">52.134</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;return value-2: &quot;</span> &lt;&lt; ret2 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> ret3 = <span class="built_in">func</span>&lt;<span class="type">int</span>&gt;(<span class="number">52.134</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;return value-3: &quot;</span> &lt;&lt; ret3 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> ret4 = <span class="built_in">func</span>&lt;<span class="type">char</span>, <span class="type">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;return value-4: &quot;</span> &lt;&lt; ret4 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当默认模板参数和模板参数自动推导同时使用时（优先级从高到低）：</p>
<ul>
<li>如果可以推导出参数类型则使用推导出的类型</li>
<li>如果函数模板无法推导出参数类型，那么编译器会使用默认模板参数</li>
<li>如果无法推导出模板参数类型并且没有设置默认模板参数，编译器就会报错。</li>
</ul>
<h3 id="7-using的使用"><a href="#7-using的使用" class="headerlink" title="7. using的使用"></a>7. using的使用</h3><h4 id="7-1-定义别名"><a href="#7-1-定义别名" class="headerlink" title="7.1 定义别名"></a>7.1 定义别名</h4><p>使用typedef定义的别名和使用using定义的别名在语义上是等效的。</p>
<p>在 C++ 中可以通过 typedef 重定义一个类型，语法格式如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> 旧的类型名 新的类型名;</span><br><span class="line"><span class="comment">// 使用举例</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">int</span> <span class="type">uint_t</span>;</span><br></pre></td></tr></table></figure>

<p>使用 using 定义别名的语法格式是这样的：(可读性更好)</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> 新的类型 = 旧的类型;</span><br><span class="line"><span class="comment">// 使用举例</span></span><br><span class="line"><span class="keyword">using</span> <span class="type">uint_t</span> = <span class="type">int</span>;</span><br></pre></td></tr></table></figure>

<p>通过 using 和 typedef 的语法格式可以看到二者的使用没有太大的区别，假设我们定义一个函数指针，using 的优势就能凸显出来了，看一下下面的例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用typedef定义函数指针</span></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span><span class="params">(*func_ptr)</span><span class="params">(<span class="type">int</span>, <span class="type">double</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用using定义函数指针</span></span><br><span class="line"><span class="keyword">using</span> func_ptr1 = <span class="built_in">int</span>(*)(<span class="type">int</span>, <span class="type">double</span>);</span><br></pre></td></tr></table></figure>

<h4 id="7-2-模板的别名"><a href="#7-2-模板的别名" class="headerlink" title="7.2 模板的别名"></a>7.2 模板的别名</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> map&lt;<span class="type">int</span>, string&gt; m1;</span><br><span class="line"><span class="keyword">typedef</span> map&lt;<span class="type">int</span>, <span class="type">int</span>&gt; m2;</span><br><span class="line"><span class="keyword">typedef</span> map&lt;<span class="type">int</span>, <span class="type">double</span>&gt; m3;</span><br><span class="line"><span class="comment">//太麻烦了，可以用using别名+template模板改进代码</span></span><br></pre></td></tr></table></figure>

<p>使用 typename 不支持给模板定义别名，这个简单的需求仅通过 typedef 很难办到,在 C++11 中，新增了一个特性就是可以通过使用 using 来为一个模板定义别名，对于上面的需求可以写成这样：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">using</span> mymap = map&lt;<span class="type">int</span>, T&gt;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// map的value指定为string类型</span></span><br><span class="line">    mymap&lt;string&gt; m;</span><br><span class="line">    m.<span class="built_in">insert</span>(<span class="built_in">make_pair</span>(<span class="number">1</span>, <span class="string">&quot;luffy&quot;</span>));</span><br><span class="line">    m.<span class="built_in">insert</span>(<span class="built_in">make_pair</span>(<span class="number">2</span>, <span class="string">&quot;ace&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// map的value指定为int类型</span></span><br><span class="line">    mymap&lt;<span class="type">int</span>&gt; m1;</span><br><span class="line">    m1.<span class="built_in">insert</span>(<span class="number">1</span>, <span class="number">100</span>);</span><br><span class="line">    m1.<span class="built_in">insert</span>(<span class="number">2</span>, <span class="number">200</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="8-委托构造函数和继承构造函数"><a href="#8-委托构造函数和继承构造函数" class="headerlink" title="8. 委托构造函数和继承构造函数"></a>8. 委托构造函数和继承构造函数</h3><h4 id="8-1-委托构造函数"><a href="#8-1-委托构造函数" class="headerlink" title="8.1 委托构造函数"></a>8.1 委托构造函数</h4><p>委托构造函数允许使用同一个类中的一个构造函数调用其它的构造函数，从而简化相关变量的初始化。下面举例说明：</p>
<p>在上面的程序中有三个构造函数，但是这三个函数中都有重复的代码，在 C++11 之前构造函数是不能调用构造函数的，加入了委托构造之后，我们就可以轻松地完成代码的优化了：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>() &#123;&#125;;</span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">int</span> max)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;m_max = max &gt; <span class="number">0</span> ? max : <span class="number">100</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">int</span> max, <span class="type">int</span> min):<span class="built_in">Test</span>(max) <span class="comment">//委托构造函数允许使用同一个类中的一个构造函数调用其它的构造函数</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;m_min = min &gt; <span class="number">0</span> &amp;&amp; min &lt; max ? min : <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">int</span> max, <span class="type">int</span> min, <span class="type">int</span> mid):<span class="built_in">Test</span>(max, min)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;m_middle = mid &lt; max &amp;&amp; mid &gt; min ? mid : <span class="number">50</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_min;</span><br><span class="line">    <span class="type">int</span> m_max;</span><br><span class="line">    <span class="type">int</span> m_middle;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">Test <span class="title">t</span><span class="params">(<span class="number">90</span>, <span class="number">30</span>, <span class="number">60</span>)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;min: &quot;</span> &lt;&lt; t.m_min &lt;&lt; <span class="string">&quot;, middle: &quot;</span> </span><br><span class="line">         &lt;&lt; t.m_middle &lt;&lt; <span class="string">&quot;, max: &quot;</span> &lt;&lt; t.m_max &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="8-2-继承构造函数"><a href="#8-2-继承构造函数" class="headerlink" title="8.2 继承构造函数"></a>8.2 继承构造函数</h4><p>C++11 中提供的继承构造函数可以让派生类直接使用基类的构造函数，而无需自己再写构造函数，尤其是在基类有很多构造函数的情况下，可以极大地简化派生类构造函数的编写。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">int</span> i) :<span class="built_in">m_i</span>(i) &#123;&#125;</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">int</span> i, <span class="type">double</span> j) :<span class="built_in">m_i</span>(i), <span class="built_in">m_j</span>(j) &#123;&#125;</span><br><span class="line">    <span class="built_in">Base</span>(<span class="type">int</span> i, <span class="type">double</span> j, string k) :<span class="built_in">m_i</span>(i), <span class="built_in">m_j</span>(j), <span class="built_in">m_k</span>(k) &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> i)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;base class: i = &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> i, string str)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;base class: i = &quot;</span> &lt;&lt; i &lt;&lt; <span class="string">&quot;, str = &quot;</span> &lt;&lt; str &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_i;</span><br><span class="line">    <span class="type">double</span> m_j;</span><br><span class="line">    string m_k;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Child</span> : <span class="keyword">public</span> Base</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">using</span> Base::Base;<span class="comment">//添加了 using Base::Base; 这样就可以在子类中直接继承父类的所有的构造函数，通过他们去构造子类对象了。</span></span><br><span class="line">    <span class="keyword">using</span> Base::func;<span class="comment">//添加了 using Base::func; 之后，就可以通过子类对象直接调用父类中被隐藏的带参 func() 函数了。</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child class: i&#x27;am luffy!!!&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">Child <span class="title">c</span><span class="params">(<span class="number">250</span>)</span></span>;</span><br><span class="line">    c.<span class="built_in">func</span>();</span><br><span class="line">    c.<span class="built_in">func</span>(<span class="number">19</span>);</span><br><span class="line">    c.<span class="built_in">func</span>(<span class="number">19</span>, <span class="string">&quot;luffy&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="9-列表初始化"><a href="#9-列表初始化" class="headerlink" title="9. 列表初始化"></a>9. 列表初始化</h3><h4 id="9-1-统一的初始化"><a href="#9-1-统一的初始化" class="headerlink" title="9.1  统一的初始化"></a>9.1  统一的初始化</h4><p>在 C++98&#x2F;03 中，对应普通数组和可以直接进行内存拷贝（memcpy ()）的对象是可以使用列表初始化来初始化数据的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数组的初始化</span></span><br><span class="line"><span class="type">int</span> array[] = &#123; <span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">9</span> &#125;;</span><br><span class="line"><span class="type">double</span> array1[<span class="number">3</span>] = &#123; <span class="number">1.2</span>, <span class="number">1.3</span>, <span class="number">1.4</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对象的初始化</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> id;</span><br><span class="line">    <span class="type">double</span> salary;</span><br><span class="line">&#125;zhang3&#123; <span class="number">1</span>, <span class="number">3000</span> &#125;;</span><br></pre></td></tr></table></figure>

<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">int</span>) &#123;&#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">const</span> Test &amp;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">Test <span class="title">t1</span><span class="params">(<span class="number">520</span>)</span></span>;</span><br><span class="line">    Test t2 = <span class="number">520</span>; </span><br><span class="line">    Test t3 = &#123; <span class="number">520</span> &#125;;</span><br><span class="line">    Test t4&#123; <span class="number">520</span> &#125;;</span><br><span class="line">    <span class="type">int</span> a1 = &#123; <span class="number">1314</span> &#125;;</span><br><span class="line">    <span class="type">int</span> a2&#123; <span class="number">1314</span> &#125;;</span><br><span class="line">    <span class="type">int</span> arr1[] = &#123; <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> &#125;;</span><br><span class="line">    <span class="type">int</span> arr2[]&#123; <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> &#125;;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//t4、a2、arr2 的写法，是 C++11 中新添加的语法格式，使用这种方式可以直接在变量名后边跟上初始化列表，来进行变量或者对象的初始化。</span></span><br></pre></td></tr></table></figure>

<p>既然使用列表初始化可以对普通类型以及对象进行直接初始化，那么在使用 new 操作符创建新对象的时候可以使用列表初始化进行对象的初始化吗？答案是肯定的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> * p = <span class="keyword">new</span> <span class="type">int</span>&#123;<span class="number">520</span>&#125;;</span><br><span class="line"><span class="type">double</span> b = <span class="type">double</span>&#123;<span class="number">52.134</span>&#125;;</span><br><span class="line"><span class="type">int</span> * array = <span class="keyword">new</span> <span class="type">int</span>[<span class="number">3</span>]&#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>&#125;;</span><br></pre></td></tr></table></figure>

<p>除此之外，列表初始化还可以直接用在函数返回值上：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Person</span>(<span class="type">int</span> id, string name)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;id: &quot;</span> &lt;&lt; id &lt;&lt; <span class="string">&quot;, name: &quot;</span> &lt;&lt; name &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">Person <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="number">9527</span>, <span class="string">&quot;华安&quot;</span> &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Person p = <span class="built_in">func</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码中的 return { 9527, “华安” }; 就相当于 return (9527, “华安” );，直接返回了一个匿名对象。通过上面的几个例子可以看出在 C++11 使用列表初始化是非常便利的，它统一了各种对象的初始化方式，而且还让代码的书写更加简单清晰。</p>
<h4 id="9-2-列表初始化细节"><a href="#9-2-列表初始化细节" class="headerlink" title="9.2 列表初始化细节"></a>9.2 列表初始化细节</h4><h5 id="9-2-1-聚合体"><a href="#9-2-1-聚合体" class="headerlink" title="9.2.1 聚合体"></a>9.2.1 聚合体</h5><p>使用列表初始化时，对于什么样的类型 C++ 会认为它是一个聚合体呢？</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> x[] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>&#125;;</span><br><span class="line"><span class="type">double</span> y[<span class="number">3</span>][<span class="number">3</span>] = &#123;</span><br><span class="line">    &#123;<span class="number">1.23</span>, <span class="number">2.34</span>, <span class="number">3.45</span>&#125;,</span><br><span class="line">    &#123;<span class="number">4.56</span>, <span class="number">5.67</span>, <span class="number">6.78</span>&#125;,</span><br><span class="line">    &#123;<span class="number">7.89</span>, <span class="number">8.91</span>, <span class="number">9.99</span>&#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">char</span> carry[] = &#123;<span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;c&#x27;</span>, <span class="string">&#x27;d&#x27;</span>, <span class="string">&#x27;e&#x27;</span>, <span class="string">&#x27;f&#x27;</span>&#125;;</span><br><span class="line">std::string sarry[] = &#123;<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;world&quot;</span>, <span class="string">&quot;nihao&quot;</span>, <span class="string">&quot;shijie&quot;</span>&#125;;</span><br></pre></td></tr></table></figure>

<ul>
<li><p>满足以下条件的类（class、struct、union）可以被看做是一个聚合类型：</p>
<ul>
<li><p>无用户自定义的构造函数。</p>
</li>
<li><p>无私有或保护的非静态数据成员。</p>
<ul>
<li><p>场景 1: 类中有私有成员，无法使用列表初始化进行初始化</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">T1</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> x;</span><br><span class="line">    <span class="type">long</span> y;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="type">int</span> z;</span><br><span class="line">&#125;t&#123; <span class="number">1</span>, <span class="number">100</span>, <span class="number">2</span>&#125;;		<span class="comment">// error, 类中有私有成员, 无法使用初始化列表初始化</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>场景 2：类中有非静态成员可以通过列表初始化进行初始化，但它不能初始化静态成员变量。</li>
</ul>
 <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">T2</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> x;</span><br><span class="line">    <span class="type">long</span> y;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> z;<span class="comment">//结构体中的静态变量 z 不能使用列表初始化进行初始化，它的初始化遵循静态成员的初始化方式。</span></span><br><span class="line">&#125;t&#123; <span class="number">1</span>, <span class="number">100</span>&#125;;		<span class="comment">// ok</span></span><br><span class="line"><span class="comment">// 静态成员的初始化</span></span><br><span class="line"><span class="type">int</span> T2::z = <span class="number">2</span>;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>无基类。</p>
</li>
<li><p>无虚函数。</p>
</li>
</ul>
</li>
</ul>
<h5 id="9-2-2-非聚合体"><a href="#9-2-2-非聚合体" class="headerlink" title="9.2.2 非聚合体"></a>9.2.2 非聚合体</h5><p>对于聚合类型的类可以直接使用列表初始化进行对象的初始化，如果不满足聚合条件还想使用列表初始化其实也是可以的，需要在类的内部自定义一个构造函数, 在构造函数中使用初始化列表对类成员变量进行初始化:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">T1</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> x;</span><br><span class="line">    <span class="type">double</span> y;</span><br><span class="line">    <span class="comment">// 在构造函数中使用初始化列表初始化类成员</span></span><br><span class="line">    <span class="built_in">T1</span>(<span class="type">int</span> a, <span class="type">double</span> b, <span class="type">int</span> c) : <span class="built_in">x</span>(a), <span class="built_in">y</span>(b), <span class="built_in">z</span>(c)&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">print</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;x: &quot;</span> &lt;&lt; x &lt;&lt; <span class="string">&quot;, y: &quot;</span> &lt;&lt; y &lt;&lt; <span class="string">&quot;, z: &quot;</span> &lt;&lt; z &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> z;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    T1 t&#123; <span class="number">520</span>, <span class="number">13.14</span>, <span class="number">1314</span> &#125;;	<span class="comment">// ok, 基于构造函数使用初始化列表初始化类成员</span></span><br><span class="line">    t.<span class="built_in">print</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="9-3-std-initializer-list"><a href="#9-3-std-initializer-list" class="headerlink" title="9.3 std::initializer_list"></a>9.3 std::initializer_list</h4><p>在 C++ 的 STL 容器中，可以进行任意长度的数据的初始化，使用初始化列表也只能进行固定参数的初始化，如果想要做到和 STL 一样有任意长度初始化的能力，可以使用 std::initializer_list 这个轻量级的类模板来实现。</p>
<p>先来介绍一下这个类模板的一些特点：</p>
<ul>
<li>它是一个轻量级的容器类型，内部定义了迭代器 iterator 等容器必须的概念，遍历时得到的迭代器是只读的。</li>
<li>对于 std::initializer_list<T> 而言，它可以接收任意长度的初始化列表，但是要求元素必须是同种类型 T</li>
<li>在 std::initializer_list 内部有三个成员接口：size(), begin(), end()。</li>
<li>std::initializer_list 对象只能被整体初始化或者赋值。</li>
</ul>
<p><strong>1.作为普通函数参数。</strong>如果想要自定义一个函数并且接收任意个数的参数（变参函数），只需要将函数参数指定为 std::initializer_list，使用初始化列表 { } 作为实参进行数据传递即可。std::initializer_list的效率是非常高的，它的内部并不负责保存初始化列表中元素的拷贝，仅仅存储了初始化列表中元素的引用。</p>
<p><strong>2.作为构造函数参数。</strong>自定义的类如果在构造对象的时候想要接收任意个数的实参，可以给构造函数指定为 std::initializer_list 类型，在自定义类的内部还是使用容器来存储接收的多个实参。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>(std::initializer_list&lt;string&gt; list)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span> it = list.<span class="built_in">begin</span>(); it != list.<span class="built_in">end</span>(); ++it)</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; *it &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">            m_names.<span class="built_in">push_back</span>(*it);</span><br><span class="line">        &#125;</span><br><span class="line">        cout &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    vector&lt;string&gt; m_names;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">Test <span class="title">t</span><span class="params">(&#123; <span class="string">&quot;jack&quot;</span>, <span class="string">&quot;lucy&quot;</span>, <span class="string">&quot;tom&quot;</span> &#125;)</span></span>;</span><br><span class="line">    <span class="function">Test <span class="title">t1</span><span class="params">(&#123; <span class="string">&quot;hello&quot;</span>, <span class="string">&quot;world&quot;</span>, <span class="string">&quot;nihao&quot;</span>, <span class="string">&quot;shijie&quot;</span> &#125;)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="10-基于范围的for循环"><a href="#10-基于范围的for循环" class="headerlink" title="10. 基于范围的for循环"></a>10. 基于范围的for循环</h3><h4 id="10-1-for循环新语法"><a href="#10-1-for循环新语法" class="headerlink" title="10.1 for循环新语法"></a>10.1 for循环新语法</h4><p>C++11 基于范围的 for 循环，语法格式：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (declaration : expression)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 循环体</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的语法格式中 declaration 表示遍历声明，在遍历过程中，当前被遍历到的元素会被存储到声明的变量中。expression 是要遍历的对象，它可以是表达式、容器、数组、初始化列表等。</p>
<p>使用基于范围的 for 循环遍历容器，示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; t&#123; <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span> &#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> value : t)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; value &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的例子中，是将容器中遍历的当前元素拷贝到了声明的变量 value 中，因此无法对容器中的元素进行写操作，如果需要在遍历过程中修改元素的值，需要使用引用。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; t&#123; <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span> &#125;;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;遍历修改之前的容器: &quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;value : t)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; value++ &lt;&lt; <span class="string">&quot; &quot;</span>;  <span class="comment">//1 2 3 4 5 6</span></span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;遍历修改之后的容器: &quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;value : t)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; value &lt;&lt; <span class="string">&quot; &quot;</span>; <span class="comment">//2 3 4 5 6 7</span></span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对容器的遍历过程中，如果只是读数据，不允许修改元素的值，可以使用 const 定义保存元素数据的变量，在定义的时候建议使用 const auto &amp;，这样相对于 const auto 效率要更高一些。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; t&#123; <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span> &#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">const</span> <span class="keyword">auto</span>&amp; value : t)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; value &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="10-2-使用细节"><a href="#10-2-使用细节" class="headerlink" title="10.2 使用细节"></a>10.2 使用细节</h4><h5 id="10-2-1-关系型容器"><a href="#10-2-1-关系型容器" class="headerlink" title="10.2.1 关系型容器"></a>10.2.1 关系型容器</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    map&lt;<span class="type">int</span>, string&gt; m&#123;</span><br><span class="line">        &#123;<span class="number">1</span>, <span class="string">&quot;lucy&quot;</span>&#125;,&#123;<span class="number">2</span>, <span class="string">&quot;lily&quot;</span>&#125;,&#123;<span class="number">3</span>, <span class="string">&quot;tom&quot;</span>&#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 基于范围的for循环方式</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; it : m)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;id: &quot;</span> &lt;&lt; it.first &lt;&lt; <span class="string">&quot;, name: &quot;</span> &lt;&lt; it.second &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 普通的for循环方式</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = m.<span class="built_in">begin</span>(); it != m.<span class="built_in">end</span>(); ++it)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;id: &quot;</span> &lt;&lt; it-&gt;first &lt;&lt; <span class="string">&quot;, name: &quot;</span> &lt;&lt; it-&gt;second &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol>
<li>使用普通的 for 循环方式（基于迭代器）遍历关联性容器， auto 自动推导出的是一个迭代器类型，需要使用迭代器的方式取出元素中的键值对（和指针的操作方法相同）：<ul>
<li>it-&gt;first</li>
<li>it-&gt;second</li>
</ul>
</li>
<li>使用基于范围的 for 循环遍历关联性容器，auto 自动推导出的类型是容器中的 value_type，相当于一个对组（std::pair）对象，提取键值对的方式如下：<ul>
<li>it.first</li>
<li>it.second</li>
</ul>
</li>
</ol>
<h5 id="10-2-2-元素只读"><a href="#10-2-2-元素只读" class="headerlink" title="10.2.2  元素只读"></a>10.2.2  元素只读</h5><p>通过对基于范围的 for 循环语法的介绍可以得知，在 for 循环内部声明一个变量的引用就可以修改遍历的表达式中的元素的值，但是这并不适用于所有的情况，对应 set 容器来说，内部元素都是只读的，这是由容器的特性决定的，因此在 for 循环中 auto &amp; 会被视为 const auto &amp; 。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;set&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    set&lt;<span class="type">int</span>&gt; st&#123; <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span> &#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;item : st) </span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; item++ &lt;&lt; endl;		<span class="comment">// error, 不能给常量赋值</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>除此之外，在遍历关联型容器时也会出现同样的问题，基于范围的for循环中，虽然可以得到一个std::pair引用，但是我们是不能修改里边的first值的，也就是key值。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    map&lt;<span class="type">int</span>, string&gt; m&#123;</span><br><span class="line">        &#123;<span class="number">1</span>, <span class="string">&quot;lucy&quot;</span>&#125;,&#123;<span class="number">2</span>, <span class="string">&quot;lily&quot;</span>&#125;,&#123;<span class="number">3</span>, <span class="string">&quot;tom&quot;</span>&#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; item : m)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// item.first 是一个常量</span></span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;id: &quot;</span> &lt;&lt; item.first++ &lt;&lt; <span class="string">&quot;, name: &quot;</span> &lt;&lt; item.second &lt;&lt; endl;  <span class="comment">// error,错误写法</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="10-2-3-访问次数"><a href="#10-2-3-访问次数" class="headerlink" title="10.2.3 访问次数"></a>10.2.3 访问次数</h5><p>基于范围的 for 循环遍历的对象可以是一个表达式或者容器 &#x2F; 数组等。假设我们对一个容器进行遍历，在遍历过程中 for 循环对这个容器的访问频率是一次还是多次呢？我们通过下面的例子验证一下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; v&#123; <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span> &#125;;</span><br><span class="line"><span class="function">vector&lt;<span class="type">int</span>&gt;&amp; <span class="title">getRange</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;get vector range...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> v;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> val : <span class="built_in">getRange</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; val &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">输出：</span><br><span class="line">get vector range...</span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">6</span></span><br></pre></td></tr></table></figure>

<p>结论：<strong>对应基于范围的 for 循环来说，冒号后边的表达式只会被执行一次。</strong>在得到遍历对象之后会先确定好迭代的范围，基于这个范围直接进行遍历。如果是普通的 for 循环，在每次迭代的时候都需要判断是否已经到了结束边界。</p>
<h3 id="11-可调用对象的包装器、绑定器"><a href="#11-可调用对象的包装器、绑定器" class="headerlink" title="11. 可调用对象的包装器、绑定器"></a>11. 可调用对象的包装器、绑定器</h3><p>如果在程序中定义了一个函数，那么在编译时系统就会为这个函数代码分配一段存储空间，这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放，这个指针变量就叫作函数指针变量，简称函数指针。</p>
<p><strong>函数指针的定义方式为：</strong><code> 函数返回值类型 (* 指针变量名) (函数参数列表);// 示例：    int(*p)(int, int);</code></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">Func</span><span class="params">(<span class="type">int</span> x)</span></span>;   <span class="comment">/*声明一个函数*/</span></span><br><span class="line"><span class="built_in">int</span> (*p) (<span class="type">int</span> x);  <span class="comment">/*定义一个函数指针*/</span></span><br><span class="line">p = Func;          <span class="comment">/*将Func函数的首地址赋给指针变量p*/</span></span><br></pre></td></tr></table></figure>

<h4 id="11-1-可调用对象"><a href="#11-1-可调用对象" class="headerlink" title="11.1 可调用对象"></a>11.1 可调用对象</h4><p>在 C++ 中存在 “可调用对象” 这么一个概念。准确来说，可调用对象有如下几种定义：</p>
<ul>
<li>1.是一个函数指针</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">print</span><span class="params">(<span class="type">int</span> a, <span class="type">double</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; a &lt;&lt; b &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 定义函数指针</span></span><br><span class="line"><span class="built_in">int</span> (*func)(<span class="type">int</span>, <span class="type">double</span>) = &amp;print;</span><br></pre></td></tr></table></figure>

<ul>
<li>2.是一个具有operator()成员函数的类对象（仿函数）</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// ()操作符重载</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">(string msg)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;msg: &quot;</span> &lt;&lt; msg &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    <span class="built_in">t</span>(<span class="string">&quot;我是要成为海贼王的男人!!!&quot;</span>);	<span class="comment">// 仿函数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>3.是一个可被转换为函数指针的类对象</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> func_ptr = <span class="built_in">void</span>(*)(<span class="type">int</span>, string);</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">print</span><span class="params">(<span class="type">int</span> a, string b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;name: &quot;</span> &lt;&lt; b &lt;&lt; <span class="string">&quot;, age: &quot;</span> &lt;&lt; a &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将类对象转换为函数指针</span></span><br><span class="line">    <span class="function"><span class="keyword">operator</span> <span class="title">func_ptr</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> print;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    <span class="comment">// 对象转换为函数指针, 并调用</span></span><br><span class="line">    <span class="built_in">t</span>(<span class="number">19</span>, <span class="string">&quot;Monkey D. Luffy&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>4.是一个类成员函数指针或者类成员指针</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">(<span class="type">int</span> a, string b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;name: &quot;</span> &lt;&lt; b &lt;&lt; <span class="string">&quot;, age: &quot;</span> &lt;&lt; a &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> m_num;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 定义类成员函数指针指向类成员函数</span></span><br><span class="line">    <span class="built_in">void</span> (Test::*func_ptr)(<span class="type">int</span>, string) = &amp;Test::print;</span><br><span class="line">    <span class="comment">// 类成员指针指向类成员变量</span></span><br><span class="line">    <span class="type">int</span> Test::*obj_ptr = &amp;Test::m_num;</span><br><span class="line"></span><br><span class="line">    Test t;</span><br><span class="line">    <span class="comment">// 通过类成员函数指针调用类成员函数</span></span><br><span class="line">    (t.*func_ptr)(<span class="number">19</span>, <span class="string">&quot;Monkey D. Luffy&quot;</span>);</span><br><span class="line">    <span class="comment">// 通过类成员指针初始化类成员变量</span></span><br><span class="line">    t.*obj_ptr = <span class="number">1</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;number is: &quot;</span> &lt;&lt; t.m_num &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的例子中满足条件的这些可调用对象对应的类型被统称为可调用类型。C++ 中的可调用类型虽然具有比较统一的操作形式，但定义方式五花八门，这样在我们试图使用统一的方式保存，或者传递一个可调用对象时会十分繁琐。现在，C++11通过提供std::function 和 std::bind统一了可调用对象的各种操作。</p>
<h4 id="11-2-包装器"><a href="#11-2-包装器" class="headerlink" title="11.2 包装器"></a>11.2 包装器</h4><p>std::function是可调用对象的包装器。它是一个类模板，可以容纳除了类成员（函数）指针之外的所有可调用对象。通过指定它的模板参数，它可以用统一的方式处理函数、函数对象、函数指针，并允许保存和延迟执行它们。</p>
<h5 id="11-2-1-基本用法"><a href="#11-2-1-基本用法" class="headerlink" title="11.2.1 基本用法"></a>11.2.1 基本用法</h5><p>std::function 必须要包含一个叫做 functional 的头文件，可调用对象包装器使用语法如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line">std::function&lt;返回值类型(参数类型列表)&gt; diy_name = 可调用对象;</span><br><span class="line"><span class="comment">//这些可调用对象有一个共同的特点，就是当做函数来调用，因为所有函数都是有返回值和参数列表的，因此需要把可调用对象返回值类型和参数列表指定出来，放到模板类型中去</span></span><br></pre></td></tr></table></figure>

<p>下面的实例代码中演示了可调用对象包装器的基本使用：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; a &lt;&lt; <span class="string">&quot; + &quot;</span> &lt;&lt; b &lt;&lt; <span class="string">&quot; = &quot;</span> &lt;&lt; a + b &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">T1</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">sub</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; a &lt;&lt; <span class="string">&quot; - &quot;</span> &lt;&lt; b &lt;&lt; <span class="string">&quot; = &quot;</span> &lt;&lt; a - b &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> a - b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">T2</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">operator</span><span class="params">()</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; a &lt;&lt; <span class="string">&quot; * &quot;</span> &lt;&lt; b &lt;&lt; <span class="string">&quot; = &quot;</span> &lt;&lt; a * b &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> a * b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 绑定一个普通函数</span></span><br><span class="line">    function&lt;<span class="type">int</span>(<span class="type">int</span>, <span class="type">int</span>)&gt; f1 = add;</span><br><span class="line">    <span class="comment">// 绑定以静态类成员函数</span></span><br><span class="line">    function&lt;<span class="type">int</span>(<span class="type">int</span>, <span class="type">int</span>)&gt; f2 = T1::sub;</span><br><span class="line">    <span class="comment">// 绑定一个仿函数</span></span><br><span class="line">    T2 t;</span><br><span class="line">    function&lt;<span class="type">int</span>(<span class="type">int</span>, <span class="type">int</span>)&gt; f3 = t;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数调用</span></span><br><span class="line">    <span class="built_in">f1</span>(<span class="number">9</span>, <span class="number">3</span>);</span><br><span class="line">    <span class="built_in">f2</span>(<span class="number">9</span>, <span class="number">3</span>);</span><br><span class="line">    <span class="built_in">f3</span>(<span class="number">9</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过测试代码可以得到结论：std::function 可以将可调用对象进行包装，得到一个统一的格式，包装完成得到的对象相当于一个函数指针，和函数指针的使用方式相同，通过包装器对象就可以完成对包装的函数的调用了。</p>
<h5 id="11-2-2-作为回调函数使用"><a href="#11-2-2-作为回调函数使用" class="headerlink" title="11.2.2 作为回调函数使用"></a>11.2.2 作为回调函数使用</h5><p>因为回调函数本身就是通过函数指针实现的，使用对象包装器可以取代函数指针的作用，来看一下下面的例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数参数是一个包装器对象</span></span><br><span class="line">    <span class="built_in">A</span>(<span class="type">const</span> function&lt;<span class="built_in">void</span>()&gt;&amp; f) : <span class="built_in">callback</span>(f)</span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">notify</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="built_in">callback</span>(); <span class="comment">// 调用通过构造函数得到的函数指针</span></span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    function&lt;<span class="type">void</span>()&gt; callback;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;我是要成为海贼王的男人!!!&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    B b;</span><br><span class="line">    <span class="function">A <span class="title">a</span><span class="params">(b)</span></span>; <span class="comment">// 仿函数通过包装器对象进行包装</span></span><br><span class="line">    a.<span class="built_in">notify</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="11-3-绑定器"><a href="#11-3-绑定器" class="headerlink" title="11.3 绑定器"></a>11.3 绑定器</h4><p>std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存，并延迟调用到任何我们需要的时候。通俗来讲，它主要有两大作用：</p>
<ol>
<li><strong>将可调用对象与其参数一起绑定成一个仿函数。</strong></li>
<li><strong>将多元（参数个数为n，n&gt;1）可调用对象转换为一元或者（n-1）元可调用对象，即只绑定部分参数。</strong></li>
</ol>
<p>绑定器函数使用语法格式如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 绑定非类成员函数/变量</span></span><br><span class="line"><span class="keyword">auto</span> f = std::<span class="built_in">bind</span>(可调用对象地址, 绑定的参数/占位符);</span><br><span class="line"><span class="comment">// 绑定类成员函/变量</span></span><br><span class="line"><span class="keyword">auto</span> f = std::<span class="built_in">bind</span>(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);</span><br></pre></td></tr></table></figure>

<p>placeholders::_1 是一个占位符，代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符 placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5 等……</p>
<p>有了占位符的概念之后，使得 std::bind 的使用变得非常灵活:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span> &lt;&lt; y &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 使用绑定器绑定可调用对象和参数, 并调用得到的仿函数</span></span><br><span class="line">    <span class="built_in">bind</span>(output, <span class="number">1</span>, <span class="number">2</span>)();</span><br><span class="line">    <span class="built_in">bind</span>(output, placeholders::_1, <span class="number">2</span>)(<span class="number">10</span>);</span><br><span class="line">    <span class="built_in">bind</span>(output, <span class="number">2</span>, placeholders::_1)(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// error, 调用时没有第二个参数</span></span><br><span class="line">    <span class="comment">// bind(output, 2, placeholders::_2)(10);  //placeholders::_2表示找到实参列表第二个实参</span></span><br><span class="line">    <span class="comment">// 调用时第一个参数10被吞掉了，没有被使用</span></span><br><span class="line">    <span class="built_in">bind</span>(output, <span class="number">2</span>, placeholders::_2)(<span class="number">10</span>, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">bind</span>(output, placeholders::_1, placeholders::_2)(<span class="number">10</span>, <span class="number">20</span>);</span><br><span class="line">    <span class="built_in">bind</span>(output, placeholders::_2, placeholders::_1)(<span class="number">10</span>, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>下面来看一个关于绑定器的实际使用的例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">callFunc</span><span class="params">(<span class="type">int</span> x, <span class="type">const</span> function&lt;<span class="type">void</span>(<span class="type">int</span>)&gt;&amp; f)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x % <span class="number">2</span> == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">f</span>(x);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output_add</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; x + <span class="number">10</span> &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 使用绑定器绑定可调用对象和参数</span></span><br><span class="line">    <span class="keyword">auto</span> f1 = <span class="built_in">bind</span>(output, placeholders::_1);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">callFunc</span>(i, f1);</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> f2 = <span class="built_in">bind</span>(output_add, placeholders::_1);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">callFunc</span>(i, f2);</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的程序中，使用了 std::bind 绑定器，在函数外部通过绑定不同的函数，控制了最后执行的结果。<strong>std::bind绑定器返回的是一个仿函数类型，得到的返回值可以直接赋值给一个std::function，在使用的时候我们并不需要关心绑定器的返回值类型，使用auto进行自动类型推导就可以了。</strong></p>
<p><strong>可调用对象包装器 std::function 是不能实现对类成员函数指针或者类成员指针的包装的，但是通过绑定器 std::bind 的配合之后，就可以完美的解决这个问题了</strong>，再来看一个例子，然后再解释里边的细节：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;x: &quot;</span> &lt;&lt; x &lt;&lt; <span class="string">&quot;, y: &quot;</span> &lt;&lt; y &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> m_number = <span class="number">100</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    <span class="comment">//1. 绑定类成员函数</span></span><br><span class="line">    function&lt;<span class="type">void</span>(<span class="type">int</span>, <span class="type">int</span>)&gt; f1 =    <span class="comment">//function中的&lt;返回值类型(参数列表)对应可调用对象中output函数的返回值类型和参数列表</span></span><br><span class="line">        <span class="built_in">bind</span>(&amp;Test::output, &amp;t, placeholders::_1, placeholders::_2);</span><br><span class="line">    <span class="comment">// 2.绑定类成员变量(公共)</span></span><br><span class="line">    function&lt;<span class="type">int</span>&amp;(<span class="type">void</span>)&gt; f2 = <span class="built_in">bind</span>(&amp;Test::m_number, &amp;t);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用</span></span><br><span class="line">    <span class="built_in">f1</span>(<span class="number">520</span>, <span class="number">1314</span>);</span><br><span class="line">    <span class="built_in">f2</span>() = <span class="number">2333</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;t.m_number: &quot;</span> &lt;&lt; t.m_number &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol>
<li><p>f1的类型是function&lt;void(int, int)&gt;，通过使用std::bind将Test的成员函数output的地址和对象t绑定，并转化为一个仿函数并存储到对象f1中。</p>
</li>
<li><p>使用绑定器绑定的类成员变量m_number得到的仿函数被存储到了类型为function&lt;int&amp;(void)&gt;的包装器对象f2中，并且可以在需要的时候修改这个成员。其中int是绑定的类成员的类型，并且允许修改绑定的变量，因此需要指定为变量的引用&amp;，由于没有参数因此参数列表指定为void。</p>
</li>
</ol>
<h3 id="12-lambda表达式"><a href="#12-lambda表达式" class="headerlink" title="12. lambda表达式"></a>12. lambda表达式</h3><h5 id="12-1-基本用法"><a href="#12-1-基本用法" class="headerlink" title="12.1 基本用法"></a>12.1 基本用法</h5><p>lambda 表达式是 C++11 最重要也是最常用的特性之一，这是现代编程语言的一个特点，lambda 表达式有如下的一些优点：</p>
<ul>
<li>声明式的编程风格：就地匿名定义目标函数或函数对象，不需要额外写一个命名函数或函数对象。</li>
<li>简洁：避免了代码膨胀和功能分散，让开发更加高效。</li>
<li>在需要的时间和地点实现功能闭包，使程序更加灵活。</li>
</ul>
<p>lambda 表达式定义了一个匿名函数，并且可以捕获一定范围内的变量。lambda 表达式的语法形式简单归纳如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[capture](params) opt -&gt; ret &#123;body;&#125;;</span><br><span class="line">其中 capture 是捕获列表，params 是参数列表，opt 是函数选项，ret 是返回值类型，body 是函数体。</span><br></pre></td></tr></table></figure>

<ol>
<li>捕获列表 []: 捕获一定范围内的变量</li>
<li>参数列表 (): 和普通函数的参数列表一样，如果没有参数参数列表可以省略不写。</li>
<li>opt 选项， 不需要可以省略<br>mutable: 可以修改按值传递进来的拷贝（注意是能修改拷贝，而不是值本身）<br> exception: 指定函数抛出的异常，如抛出整数类型的异常，可以使用 throw ();<br>4.返回值类型：在 C++11 中，lambda 表达式的返回值是通过返回值后置语法来定义的。</li>
<li>函数体：函数的实现，这部分不能省略，但函数体可以为空。</li>
</ol>
<h5 id="12-2-捕获列表"><a href="#12-2-捕获列表" class="headerlink" title="12.2 捕获列表"></a>12.2 捕获列表</h5><p>lambda 表达式的捕获列表可以捕获一定范围内的变量，具体使用方式如下：</p>
<ul>
<li>[] - 不捕捉任何变量</li>
<li>[&amp;] - 捕获外部作用域中所有变量，并作为引用在函数体内使用 (按引用捕获)</li>
<li>[&#x3D;] - 捕获外部作用域中所有变量，并作为副本在函数体内使用 (按值捕获)<ul>
<li>拷贝的副本在匿名函数体内部是只读的</li>
</ul>
</li>
<li>[&#x3D;, &amp;foo] - 按值捕获外部作用域中所有变量，并按照引用捕获外部变量 foo</li>
<li>[bar] - 按值捕获 bar 变量，同时不捕获其他变量</li>
<li>[&amp;bar] - 按引用捕获 bar 变量，同时不捕获其他变量</li>
<li>[this] - 捕获当前类中的 this 指针<ul>
<li>让 lambda 表达式拥有和当前类成员函数同样的访问权限</li>
<li>如果已经使用了 &amp; 或者 &#x3D;, 默认添加此选项</li>
</ul>
</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">auto</span> x1 = [] &#123;<span class="keyword">return</span> m_number; &#125;;                      <span class="comment">// error</span></span><br><span class="line">        <span class="keyword">auto</span> x2 = [=] &#123;<span class="keyword">return</span> m_number + x + y; &#125;;             <span class="comment">// ok,   x,y属于匿名函数函数体以外的变量</span></span><br><span class="line">        <span class="keyword">auto</span> x3 = [&amp;] &#123;<span class="keyword">return</span> m_number + x + y; &#125;;             <span class="comment">// ok</span></span><br><span class="line">        <span class="keyword">auto</span> x4 = [<span class="keyword">this</span>] &#123;<span class="keyword">return</span> m_number; &#125;;                  <span class="comment">// ok</span></span><br><span class="line">        <span class="keyword">auto</span> x5 = [<span class="keyword">this</span>] &#123;<span class="keyword">return</span> m_number + x + y; &#125;;          <span class="comment">// error</span></span><br><span class="line">        <span class="keyword">auto</span> x6 = [<span class="keyword">this</span>, x, y] &#123;<span class="keyword">return</span> m_number + x + y; &#125;;    <span class="comment">// ok</span></span><br><span class="line">        <span class="keyword">auto</span> x7 = [<span class="keyword">this</span>] &#123;<span class="keyword">return</span> m_number++; &#125;;                <span class="comment">// ok</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> m_number = <span class="number">100</span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//如果我们想引用匿名函数体以外的变量，就需要在中括号[]中添加对外部变量的捕捉方式。</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">x1：错误，没有捕获外部变量，不能使用类成员 m_number</span></span><br><span class="line"><span class="comment">x2：正确，以值拷贝的方式捕获所有外部变量</span></span><br><span class="line"><span class="comment">x3：正确，以引用的方式捕获所有外部变量</span></span><br><span class="line"><span class="comment">x4：正确，捕获 this 指针，可访问对象内部成员</span></span><br><span class="line"><span class="comment">x5：错误，捕获 this 指针，可访问类内部成员，没有捕获到变量 x，y，因此不能访问。</span></span><br><span class="line"><span class="comment">x6：正确，捕获 this 指针，x，y</span></span><br><span class="line"><span class="comment">x7：正确，捕获 this 指针，并且可以修改对象内部变量的值</span></span><br><span class="line"><span class="comment"></span></span><br></pre></td></tr></table></figure>

<p><strong>在匿名函数内部，需要通过 lambda 表达式的捕获列表控制如何捕获外部变量，以及访问哪些变量。默认状态下 lambda 表达式无法修改通过复制方式捕获外部变量，如果希望修改这些外部变量，需要通过引用的方式进行捕获。</strong></p>
<h5 id="12-3-返回值"><a href="#12-3-返回值" class="headerlink" title="12.3 返回值"></a>12.3 返回值</h5><p>很多时候，lambda 表达式的返回值是非常明显的，因此在 C++11 中允许省略 lambda 表达式的返回值。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 完整的lambda表达式定义</span></span><br><span class="line"><span class="keyword">auto</span> f = [](<span class="type">int</span> a) -&gt; <span class="type">int</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> a+<span class="number">10</span>;  </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 忽略返回值的lambda表达式定义</span></span><br><span class="line"><span class="keyword">auto</span> f = [](<span class="type">int</span> a)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> a+<span class="number">10</span>;  </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>一般情况下，不指定 lambda 表达式的返回值，编译器会根据 return 语句自动推导返回值的类型，但需要注意的是 labmda表达式不能通过列表初始化自动推导出返回值类型。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ok，可以自动推导出返回值类型</span></span><br><span class="line"><span class="keyword">auto</span> f = [](<span class="type">int</span> i)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// error，不能推导出返回值类型</span></span><br><span class="line"><span class="keyword">auto</span> f1 = []()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="number">1</span>, <span class="number">2</span>&#125;;	<span class="comment">// 基于列表初始化推导返回值，错误</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="12-4-函数本质"><a href="#12-4-函数本质" class="headerlink" title="12.4 函数本质"></a>12.4 函数本质</h5><p>如果从广义上说，lambda表达式产生的是也是一种函数对象，因为它也是直接使用()来传递参数进行调用的。</p>
<p>使用 lambda 表达式捕获列表捕获外部变量，如果希望去修改按值捕获的外部变量，那么应该如何处理呢？这就需要使用 mutable 选项，被mutable修改是lambda表达式就算没有参数也要写明参数列表，并且可以去掉按值捕获的外部变量的只读（const）属性。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">auto</span> f1 = [=] &#123;<span class="keyword">return</span> a++; &#125;;              <span class="comment">// error, 按值捕获外部变量, a是只读的</span></span><br><span class="line"><span class="keyword">auto</span> f2 = [=]()<span class="keyword">mutable</span> &#123;<span class="keyword">return</span> a++; &#125;;     <span class="comment">// ok</span></span><br></pre></td></tr></table></figure>

<p>最后再剖析一下为什么通过值拷贝的方式捕获的外部变量是只读的:</p>
<ol>
<li><strong>lambda表达式的类型在C++11中会被看做是一个带operator()的类，即仿函数。</strong></li>
<li>按照C++标准，lambda表达式的operator()默认是const的，一个const成员函数是无法修改成员变量值的。</li>
</ol>
<p><strong>mutable 选项的作用就在于取消 operator () 的 const 属性。</strong></p>
<p><strong>因为 lambda 表达式在 C++ 中会被看做是一个仿函数，因此可以使用std::function和std::bind来存储和操作lambda表达式</strong>：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 包装可调用函数</span></span><br><span class="line">    std::function&lt;<span class="type">int</span>(<span class="type">int</span>)&gt; f1 = [](<span class="type">int</span> a) &#123;<span class="keyword">return</span> a; &#125;;</span><br><span class="line">    <span class="comment">// 绑定可调用函数</span></span><br><span class="line">    std::function&lt;<span class="type">int</span>(<span class="type">int</span>)&gt; f2 = <span class="built_in">bind</span>([](<span class="type">int</span> a) &#123;<span class="keyword">return</span> a; &#125;, placeholders::_1);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数调用</span></span><br><span class="line">    cout &lt;&lt; <span class="built_in">f1</span>(<span class="number">100</span>) &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="built_in">f2</span>(<span class="number">200</span>) &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于没有捕获任何变量的 lambda 表达式，还可以转换成一个普通的函数指针,反之，捕获了任何变量都不能看做是函数指针：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> func_ptr = <span class="built_in">int</span>(*)(<span class="type">int</span>);</span><br><span class="line"><span class="comment">// 没有捕获任何外部变量的匿名函数</span></span><br><span class="line">func_ptr f = [](<span class="type">int</span> a)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> a;  </span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 函数调用</span></span><br><span class="line"><span class="built_in">f</span>(<span class="number">1314</span>);</span><br></pre></td></tr></table></figure>

<h3 id="13-右值引用"><a href="#13-右值引用" class="headerlink" title="13. 右值引用"></a>13. 右值引用</h3><h4 id="13-1-右值引用"><a href="#13-1-右值引用" class="headerlink" title="13.1 右值引用"></a>13.1 右值引用</h4><h5 id="13-1-1-右值"><a href="#13-1-1-右值" class="headerlink" title="13.1.1 右值"></a>13.1.1 右值</h5><p>C++11 增加了一个新的类型，称为右值引用（ R-value reference），标记为 &amp;&amp;。在介绍右值引用类型之前先要了解什么是左值和右值：</p>
<ul>
<li><p>lvalue 是 loactor value 的缩写，rvalue 是 read value 的缩写</p>
</li>
<li><p>左值是指存储在内存中、有明确存储地址（<strong>可取地址</strong>）的数据；</p>
</li>
<li><p>右值是指可以提供数据值的数据（<strong>不可取地址</strong>）；</p>
</li>
</ul>
<p><strong>区分左值与右值的便捷方法是：可以对表达式取地址（&amp;）就是左值，否则为右值 。</strong></p>
<p>C++11 中<strong>右值</strong>可以分为两种：一个是<strong>将亡值</strong>（ xvalue, expiring value），另一个则是<strong>纯右值</strong>（ prvalue, PureRvalue）：</p>
<ul>
<li>纯右值：非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和 lambda 表达式等</li>
<li>将亡值：与右值引用相关的表达式，比如，T&amp;&amp; 类型函数的返回值、 std::move 的返回值等。</li>
</ul>
<h5 id="13-1-2-右值引用"><a href="#13-1-2-右值引用" class="headerlink" title="13.1.2 右值引用"></a>13.1.2 右值引用</h5><p>右值引用就是对一个右值进行引用的类型。因为右值是匿名的，所以我们只能通过引用的方式找到它。无论声明左值引用还是右值引用都必须立即进行初始化，因为引用类型本身并不拥有所绑定对象的内存，只是该对象的一个别名。通过右值引用的声明，该右值又“重获新生”，其生命周期与右值引用类型变量的生命周期一样，只要该变量还活着，该右值临时量将会一直存活下去。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span>&amp;&amp; value = <span class="number">520</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;construct: my name is jerry&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">const</span> Test&amp; a)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;copy construct: my name is tom&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">Test <span class="title">getObj</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Test</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> a1;</span><br><span class="line">    <span class="type">int</span> &amp;&amp;a2 = a1;        <span class="comment">// error</span></span><br><span class="line">    Test&amp; t = <span class="built_in">getObj</span>();   <span class="comment">// error</span></span><br><span class="line">    Test &amp;&amp; t = <span class="built_in">getObj</span>();</span><br><span class="line">    <span class="type">const</span> Test&amp; t = <span class="built_in">getObj</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>在 int &amp;&amp;a2 &#x3D; a1; 中 a1 虽然写在了 &#x3D; 右边，但是它仍然是一个左值，使用左值初始化一个右值引用类型是不合法的。</li>
<li>在 Test&amp; t &#x3D; getObj() 这句代码中语法是错误的，右值不能给普通的左值引用赋值。</li>
<li>在 Test &amp;&amp; t &#x3D; getObj(); 中 getObj() 返回的临时对象被称之为将亡值，t 是这个将亡值的右值引用。</li>
<li>const Test&amp; t &#x3D; getObj() 这句代码的语法是正确的，常量左值引用是一个万能引用类型，它可以接受左值、右值、常量左值和常量右值。</li>
</ul>
<h4 id="13-2-性能优化"><a href="#13-2-性能优化" class="headerlink" title="13.2 性能优化"></a>13.2 性能优化</h4><p>在 C++ 中在进行对象赋值操作的时候，很多情况下会发生对象之间的深拷贝，如果堆内存很大，这个拷贝的代价也就非常大，在某些情况下，如果想要避免对象的深拷贝，就可以使用右值引用进行性能的优化。</p>
<p>再来修改一下上面的实例代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>() : <span class="built_in">m_num</span>(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">100</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;construct: my name is jerry&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">const</span> Test&amp; a) : <span class="built_in">m_num</span>(<span class="keyword">new</span> <span class="built_in">int</span>(*a.m_num))</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;copy construct: my name is tom&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ~<span class="built_in">Test</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">delete</span> m_num;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span>* m_num;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">Test <span class="title">getObj</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t = <span class="built_in">getObj</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;t.m_num: &quot;</span> &lt;&lt; *t.m_num &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>通过输出的结果可以看到调用 Test t &#x3D; getObj(); 的时候调用拷贝构造函数对返回的临时对象进行了深拷贝得到了对象 t，在 getObj() 函数中创建的对象虽然进行了内存的申请操作，但是没有使用就释放掉了。如果能够使用临时对象已经申请的资源，既能节省资源，还能节省资源申请和释放的时间，如果要执行这样的操作就需要使用右值引用了，<strong>右值引用具有移动语义，移动语义可以将资源（堆、系统对象等）通过浅拷贝从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建、拷贝以及销毁，可以大幅提高 C++ 应用程序的性能。</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>() : <span class="built_in">m_num</span>(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">100</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;construct: my name is jerry&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">const</span> Test&amp; a) : <span class="built_in">m_num</span>(<span class="keyword">new</span> <span class="built_in">int</span>(*a.m_num))</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;copy construct: my name is tom&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 添加移动构造函数</span></span><br><span class="line">    <span class="built_in">Test</span>(Test&amp;&amp; a) : <span class="built_in">m_num</span>(a.m_num)</span><br><span class="line">    &#123;</span><br><span class="line">        a.m_num = <span class="literal">nullptr</span>;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;move construct: my name is sunny&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ~<span class="built_in">Test</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">delete</span> m_num;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;destruct Test class ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span>* m_num;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">Test <span class="title">getObj</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t = <span class="built_in">getObj</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;t.m_num: &quot;</span> &lt;&lt; *t.m_num &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>通过修改，在上面的代码给 Test 类添加了移动构造函数（参数为右值引用类型），这样在进行 Test t &#x3D; getObj(); 操作的时候并没有调用拷贝构造函数进行深拷贝，而是调用了移动构造函数，在这个函数中只是进行了浅拷贝，没有对临时对象进行深拷贝，提高了性能。</p>
<p>在测试程序中 getObj() 的返回值就是一个将亡值，也就是说是一个右值，在进行赋值操作的时候如果 &#x3D; 右边是一个右值，那么移动构造函数就会被调用。<strong>移动构造中使用了右值引用，会将临时对象中的堆内存地址的所有权转移给对象t，这块内存被成功续命，因此在t对象中还可以继续使用这块内存。</strong></p>
<h4 id="临时对象："><a href="#临时对象：" class="headerlink" title="临时对象："></a>临时对象：</h4><p><strong>注：1.建立一个没有命名的非堆（non-heap）对象，也就是匿名对象时，会产生临时对象。</strong></p>
<p>​       <strong>2.构造函数作为隐式类型转换函数时，会创建临时对象，用作实参传递给函数。</strong> </p>
<p>​     <strong>3.函数返回一个对象时，会产生临时对象。</strong></p>
<h4 id="13-3-amp-amp-的特性"><a href="#13-3-amp-amp-的特性" class="headerlink" title="13.3 &amp;&amp;的特性"></a>13.3 &amp;&amp;的特性</h4><p>在 C++ 中，并不是所有情况下 &amp;&amp; 都代表是一个右值引用，具体的场景体现在模板和自动类型推导中，如果是模板参数需要指定为 T&amp;&amp;，如果是自动类型推导需要指定为 auto &amp;&amp;，在这两种场景下 &amp;&amp; 被称作未定的引用类型。另外还有一点需要额外注意 const T&amp;&amp; 表示一个右值引用，不是未定引用类型。</p>
<p>例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> x = <span class="number">520</span>, y = <span class="number">1314</span>;</span><br><span class="line"> <span class="number">4</span>   <span class="keyword">auto</span>&amp;&amp; v1 = x;      <span class="comment">//第 4 行中 auto&amp;&amp; 表示一个整形的左值引用</span></span><br><span class="line"> <span class="number">5</span>   <span class="keyword">auto</span>&amp;&amp; v2 = <span class="number">250</span>;    <span class="comment">//第 5 行中 auto&amp;&amp; 表示一个整形的右值引用</span></span><br><span class="line"> <span class="number">6</span>   <span class="keyword">decltype</span>(x)&amp;&amp; v3 = y;   <span class="comment">// error</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;v1: &quot;</span> &lt;&lt; v1 &lt;&lt; <span class="string">&quot;, v2: &quot;</span> &lt;&lt; v2 &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<ul>
<li>第 6 行中 decltype(x)&amp;&amp; 等价于 int&amp;&amp; 是一个右值引用不是未定引用类型，y 是一个左值，不能使用左值初始化一个右值引用类型。</li>
</ul>
<p>由于上述代码中存在 T&amp;&amp; 或者 auto&amp;&amp; 这种未定引用类型，当它作为参数时，有可能被一个右值引用初始化，也有可能被一个左值引用初始化，在进行类型推导时右值引用类型（&amp;&amp;）会发生变化，这种变化被称为引用折叠。<strong>在 C++11 中引用折叠的规则如下：</strong></p>
<ul>
<li><p><strong>通过右值推导 T&amp;&amp; 或者 auto&amp;&amp; 得到的是一个右值引用类型</strong></p>
</li>
<li><p><strong>通过非右值（右值引用、左值、左值引用、常量右值引用、常量左值引用）推导 T&amp;&amp; 或者 auto&amp;&amp; 得到的是一个左值引用类型</strong></p>
</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>&amp;&amp; a1 = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; bb = a1; <span class="comment">//a1 为右值引用，推导出的 bb 为左值引用类型</span></span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; bb1 = <span class="number">5</span>; <span class="comment">//5 为右值，推导出的 bb1 为右值引用类型</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> a2 = <span class="number">5</span>;       <span class="comment">//</span></span><br><span class="line"><span class="type">int</span> &amp;a3 = a2;</span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; cc = a3;     <span class="comment">//a3 为左值引用，推导出的 cc 为左值引用类型	</span></span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; cc1 = a2;    <span class="comment">//a2 为左值，推导出的 cc1 为左值引用类型</span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span>&amp; s1 = <span class="number">100</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span>&amp;&amp; s2 = <span class="number">100</span>;</span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; dd = s1;    <span class="comment">//s1 为常量左值引用，推导出的 dd 为常量左值引用类型</span></span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; ee = s2;    <span class="comment">//s2 为常量右值引用，推导出的 ee 为常量左值引用类型</span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="keyword">auto</span>&amp;&amp; x = <span class="number">5</span>;  <span class="comment">//x 为右值引用，不需要推导，只能通过右值初始化</span></span><br></pre></td></tr></table></figure>

<p>最后一个例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printValue</span><span class="params">(<span class="type">int</span> &amp;i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;l-value: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printValue</span><span class="params">(<span class="type">int</span> &amp;&amp;i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;r-value: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">forward</span><span class="params">(<span class="type">int</span> &amp;&amp;k)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printValue</span>(k);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> i = <span class="number">520</span>;</span><br><span class="line">    <span class="built_in">printValue</span>(i);  <span class="comment">//左值引用，调用第一个printValue</span></span><br><span class="line">    <span class="built_in">printValue</span>(<span class="number">1314</span>);<span class="comment">//右值引用，调用第二个</span></span><br><span class="line">    forward(<span class="number">250</span>); <span class="comment">//编译器会把printValue(k)中的k看做是左值引用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>最后总结一下关于 &amp;&amp; 的使用：</p>
<ol>
<li><strong>左值和右值是独立于他们的类型的，右值引用类型可能是左值也可能是右值。</strong></li>
<li><strong>编译器会将已命名的右值引用视为左值，将未命名的右值引用视为右值。</strong></li>
<li><strong>auto&amp;&amp;或者函数参数类型自动推导的T&amp;&amp;是一个未定的引用类型，它可能是左值引用也可能是右值引用类型，这取决于初始化的值类型（上面有例子）。</strong></li>
<li><strong>通过右值推导 T&amp;&amp; 或者 auto&amp;&amp; 得到的是一个右值引用类型，其余都是左值引用类型。</strong></li>
</ol>
<h3 id="14-转移和完美转发"><a href="#14-转移和完美转发" class="headerlink" title="14. 转移和完美转发"></a>14. 转移和完美转发</h3><h4 id="14-1-move"><a href="#14-1-move" class="headerlink" title="14.1 move"></a>14.1 move</h4><p>在 C++11 添加了右值引用，并且不能使用左值初始化右值引用，如果想要使用左值初始化一个右值引用需要借助 std::move () 函数，使用std::move方法可以将左值转换为右值。使用这个函数并不能移动任何东西，而是和移动构造函数一样都具有移动语义，将对象的状态或者所有权从一个对象转移到另一个对象，只是转移，没有内存拷贝。</p>
<p>使用方法如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>：</span><br><span class="line">    <span class="built_in">Test</span>()&#123;&#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Test t;</span><br><span class="line">    Test &amp;&amp; v1 = t;          <span class="comment">// error 使用左值初始化右值引用，因此语法是错误的</span></span><br><span class="line">    Test &amp;&amp; v2 = <span class="built_in">move</span>(t);    <span class="comment">// ok 使用 move() 函数将左值转换为了右值，这样就可以初始化右值引用了。</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>假设一个临时容器很大，并且需要将这个容器赋值给另一个容器，就可以执行如下操作：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">list&lt;string&gt; ls;</span><br><span class="line">ls.<span class="built_in">push_back</span>(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line">ls.<span class="built_in">push_back</span>(<span class="string">&quot;world&quot;</span>);</span><br><span class="line">......</span><br><span class="line">list&lt;string&gt; ls1 = ls;        <span class="comment">// 需要拷贝, 效率低</span></span><br><span class="line">list&lt;string&gt; ls2 = <span class="built_in">move</span>(ls); <span class="comment">//把要释放的对象ls的资源转移给其他对象ls2,减少拷贝次数，提供允许效率</span></span><br></pre></td></tr></table></figure>

<p>如果不使用 std::move，拷贝的代价很大，性能较低。使用 move 几乎没有任何代价，只是转换了资源的所有权。如果一个对象内部有较大的堆内存或者动态数组时，使用 move () 就可以非常方便的进行数据所有权的转移。另外，我们也可以给类编写相应的移动构造函数（T::T(T&amp;&amp; another)）和和具有移动语义的赋值函数（T&amp;&amp; T::operator&#x3D;(T&amp;&amp; rhs)），在构造对象和赋值的时候尽可能的进行资源的重复利用，因为它们都是接收一个右值引用参数。</p>
<h4 id="14-2-forward"><a href="#14-2-forward" class="headerlink" title="14.2 forward"></a>14.2 forward</h4><p>右值引用类型是独立于值的，一个右值引用作为函数参数的形参时，在函数内部转发该参数给内部其他函数时，它就变成一个左值，并不是原来的类型了。如果需要按照参数原来的类型转发到另一个函数，可以使用 C++11 提供的 std::forward () 函数，该函数实现的功能称之为完美转发。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 函数原型</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt; <span class="function">T&amp;&amp; <span class="title">forward</span> <span class="params">(<span class="keyword">typename</span> remove_reference&lt;T&gt;::type&amp; t)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt; <span class="function">T&amp;&amp; <span class="title">forward</span> <span class="params">(<span class="keyword">typename</span> remove_reference&lt;T&gt;::type&amp;&amp; t)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 精简之后的样子</span></span><br><span class="line">std::forward&lt;T&gt;(t);</span><br></pre></td></tr></table></figure>

<ul>
<li>当T为<strong>左值引用</strong>类型时，t将被转换为T类型的<strong>左值</strong></li>
<li>当T<strong>不是左值引用</strong>类型时，t将被转换为T类型的<strong>右值</strong></li>
</ul>
<p>下面通过一个例子演示一下关于 forward 的使用:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printValue</span><span class="params">(T&amp; t)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;l-value: &quot;</span> &lt;&lt; t &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printValue</span><span class="params">(T&amp;&amp; t)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;r-value: &quot;</span> &lt;&lt; t &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">testForward</span><span class="params">(T &amp;&amp; v)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printValue</span>(v);  </span><br><span class="line">    <span class="built_in">printValue</span>(<span class="built_in">move</span>(v)); </span><br><span class="line">    <span class="built_in">printValue</span>(forward&lt;T&gt;(v)); </span><br><span class="line">    cout &lt;&lt; endl; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">testForward</span>(<span class="number">520</span>); <span class="comment">//左 右 右</span></span><br><span class="line">    <span class="type">int</span> num = <span class="number">1314</span>;</span><br><span class="line">    <span class="built_in">testForward</span>(num);   <span class="comment">//左 右 左</span></span><br><span class="line">    <span class="built_in">testForward</span>(forward&lt;<span class="type">int</span>&gt;(num));  <span class="comment">//左 右 右</span></span><br><span class="line">    <span class="built_in">testForward</span>(forward&lt;<span class="type">int</span>&amp;&gt;(num));  <span class="comment">//左 右 左</span></span><br><span class="line">    <span class="built_in">testForward</span>(forward&lt;<span class="type">int</span>&amp;&amp;&gt;(num)); <span class="comment">//左 右 右</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>testForward(forward&lt;int&amp;&gt;(num));forward 的模板类型为 int&amp;，最终会得到一个左值，函数的形参为未定引用类型 T&amp;&amp; 被左值初始化后得到一个左值引用类型<ul>
<li>printValue(v); 实参为左值</li>
<li>printValue(move(v)); 通过 move 将左值转换为右值，实参为右值</li>
<li>printValue(forward<T>(v));forward 的模板参数为左值引用，最终得到一个左值，实参为左值</li>
</ul>
</li>
<li>testForward(forward&lt;int&amp;&amp;&gt;(num));forward 的模板类型为 int&amp;&amp;，最终会得到一个右值，函数的形参为未定引用类型 T&amp;&amp; 被右值初始化后得到一个右值引用类型<ul>
<li>printValue(v); 已命名的右值 v，编译器会视为左值处理，实参为左值</li>
<li>printValue(move(v)); 已命名的右值编译器会视为左值处理，通过 move 又将其转换为右值，实参为右值</li>
<li>printValue(forward<T>(v));forward 的模板参数为右值引用，最终得到一个右值，实参为右值</li>
</ul>
</li>
</ul>
<h3 id="15-共享智能指针"><a href="#15-共享智能指针" class="headerlink" title="15. 共享智能指针"></a>15. 共享智能指针</h3><p>在 C++ 中没有垃圾回收机制，必须自己释放分配的内存，否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针（smart pointer）。<strong>智能指针是存储指向动态分配（堆）对象指针的类，用于生存期的控制，能够确保在离开指针所在作用域时，自动地销毁动态分配的对象，防止内存泄露。智能指针的核心实现技术是引用计数，每使用它一次，内部引用计数加1，每析构一次内部的引用计数减1，减为0时，删除所指向的堆内存。</strong></p>
<p>C++11 中提供了三种智能指针，使用这些智能指针时需要引用头文件 <memory>：</p>
<ul>
<li>std::shared_ptr：共享的智能指针</li>
<li>std::unique_ptr：独占的智能指针</li>
<li>std::weak_ptr：弱引用的智能指针，它不共享指针，不能操作资源，是用来监视 shared_ptr 的。</li>
</ul>
<h4 id="15-1-shared-ptr的初始化"><a href="#15-1-shared-ptr的初始化" class="headerlink" title="15.1 shared_ptr的初始化"></a>15.1 shared_ptr的初始化</h4><p>共享智能指针是指多个智能指针可以同时管理同一块有效的内存，共享智能指针 shared_ptr 是一个模板类，如果要进行初始化有三种方式：通过构造函数、std::make_shared 辅助函数以及 reset 方法。共享智能指针对象初始化完毕之后就指向了要管理的那块堆内存，如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数 use_count，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 管理当前对象的 shared_ptr 实例数量，或若无被管理对象则为 0。</span></span><br><span class="line"><span class="function"><span class="type">long</span> <span class="title">use_count</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<h5 id="15-1-1-通过构造函数初始化"><a href="#15-1-1-通过构造函数初始化" class="headerlink" title="15.1.1 通过构造函数初始化"></a>15.1.1 通过构造函数初始化</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// shared_ptr&lt;T&gt; 类模板中，提供了多种实用的构造函数, 语法格式如下:</span></span><br><span class="line">std::shared_ptr&lt;T&gt; 智能指针名字(创建堆内存);</span><br></pre></td></tr></table></figure>

<p>示例代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 使用智能指针管理一块 int 型的堆内存</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">520</span>))</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr1管理的内存引用计数: &quot;</span> &lt;&lt; ptr1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 使用智能指针管理一块字符数组对应的堆内存</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">char</span>&gt; <span class="title">ptr2</span><span class="params">(<span class="keyword">new</span> <span class="type">char</span>[<span class="number">12</span>])</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr2管理的内存引用计数: &quot;</span> &lt;&lt; ptr2.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 创建智能指针对象, 不管理任何内存</span></span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr3;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr3管理的内存引用计数: &quot;</span> &lt;&lt; ptr3.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 创建智能指针对象, 初始化为空</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr4</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr4管理的内存引用计数: &quot;</span> &lt;&lt; ptr4.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*输出：</span></span><br><span class="line"><span class="comment">ptr1管理的内存引用计数: 1</span></span><br><span class="line"><span class="comment">ptr2管理的内存引用计数: 1</span></span><br><span class="line"><span class="comment">ptr3管理的内存引用计数: 0</span></span><br><span class="line"><span class="comment">ptr4管理的内存引用计数: 0 */</span></span><br></pre></td></tr></table></figure>

<p>如果智能指针被初始化了一块有效内存，那么这块内存的引用计数 + 1，如果智能指针没有被初始化或者被初始化为 nullptr 空指针，引用计数不会 + 1。另外，不要使用一个原始指针初始化多个 shared_ptr。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> *p = <span class="keyword">new</span> <span class="type">int</span>;</span><br><span class="line"><span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">p1</span><span class="params">(p)</span></span>;</span><br><span class="line"><span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">p2</span><span class="params">(p)</span></span>;		<span class="comment">// error, 编译不会报错, 运行会出错</span></span><br></pre></td></tr></table></figure>

<h5 id="15-1-2-通过拷贝和移动构造函数初始化"><a href="#15-1-2-通过拷贝和移动构造函数初始化" class="headerlink" title="15.1.2 通过拷贝和移动构造函数初始化"></a>15.1.2 通过拷贝和移动构造函数初始化</h5><p>当一个智能指针被初始化之后，就可以通过这个智能指针初始化其他新对象。在创建新对象的时候，对应的拷贝构造函数或者移动构造函数就被自动调用了。</p>
<p><strong>调用移动构造需要右值引用，右值引用有需要右值进行初始化，怎么得到一个右值（纯右值或将亡值）呢？通过move()函数便可以得到一个右值-&gt;将亡值.</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">520</span>))</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr1管理的内存引用计数: &quot;</span> &lt;&lt; ptr1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">//调用拷贝构造函数</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr2</span><span class="params">(ptr1)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr2管理的内存引用计数: &quot;</span> &lt;&lt; ptr2.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr3 = ptr1;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr3管理的内存引用计数: &quot;</span> &lt;&lt; ptr3.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">//调用移动构造函数</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr4</span><span class="params">(std::move(ptr1))</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr4管理的内存引用计数: &quot;</span> &lt;&lt; ptr4.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    std::shared_ptr&lt;<span class="type">int</span>&gt; ptr5 = std::<span class="built_in">move</span>(ptr2);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr5管理的内存引用计数: &quot;</span> &lt;&lt; ptr5.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*输出：</span></span><br><span class="line"><span class="comment">ptr1管理的内存引用计数: 1</span></span><br><span class="line"><span class="comment">ptr2管理的内存引用计数: 2</span></span><br><span class="line"><span class="comment">ptr3管理的内存引用计数: 3</span></span><br><span class="line"><span class="comment">ptr4管理的内存引用计数: 3</span></span><br><span class="line"><span class="comment">ptr5管理的内存引用计数: 3</span></span><br></pre></td></tr></table></figure>

<p><strong>如果使用拷贝的方式初始化共享智能指针对象，这两个对象会同时管理同一块堆内存，堆内存对应的引用计数也会增加；如果使用移动的方式初始智能指针对象，只是转让了内存的所有权，管理内存的对象并不会增加，因此内存的引用计数不会变化。</strong></p>
<h5 id="15-1-3-通过-std-make-shared-初始化"><a href="#15-1-3-通过-std-make-shared-初始化" class="headerlink" title="15.1.3 通过 std::make_shared 初始化"></a>15.1.3 通过 std::make_shared 初始化</h5><p>通过 C++ 提供的 std::make_shared() 就可以完成内存对象的创建并将其初始化给智能指针，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> T, <span class="keyword">class</span>... Args &gt;</span></span><br><span class="line"><span class="function">shared_ptr&lt;T&gt; <span class="title">make_shared</span><span class="params">( Args&amp;&amp;... args )</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">T：模板参数的数据类型</span></span><br><span class="line"><span class="comment">Args&amp;&amp;... args ：要初始化的数据，如果是通过 make_shared 创建对象，需按照构造函数的参数列表指定</span></span><br></pre></td></tr></table></figure>

<p>示例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Test</span>() </span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;construct Test...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">Test</span>(<span class="type">int</span> x) </span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;construct Test, x = &quot;</span> &lt;&lt; x &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">Test</span>(string str) </span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;construct Test, str = &quot;</span> &lt;&lt; str &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    ~<span class="built_in">Test</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;destruct Test ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1</span></span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr1 = <span class="built_in">make_shared</span>&lt;<span class="type">int</span>&gt;(<span class="number">520</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr1管理的内存引用计数: &quot;</span> &lt;&lt; ptr1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    shared_ptr&lt;Test&gt; ptr2 = <span class="built_in">make_shared</span>&lt;Test&gt;();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr2管理的内存引用计数: &quot;</span> &lt;&lt; ptr2.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    shared_ptr&lt;Test&gt; ptr3 = <span class="built_in">make_shared</span>&lt;Test&gt;(<span class="number">520</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr3管理的内存引用计数: &quot;</span> &lt;&lt; ptr3.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    shared_ptr&lt;Test&gt; ptr4 = <span class="built_in">make_shared</span>&lt;Test&gt;(<span class="string">&quot;我是要成为海贼王的男人!!!&quot;</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr4管理的内存引用计数: &quot;</span> &lt;&lt; ptr4.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>使用 std::make_shared() 模板函数可以完成内存地址的创建，并将最终得到的内存地址传递给共享智能指针对象管理。如果申请的内存是普通类型，通过函数的（）可完成地址的初始化，如果要创建一个类对象，函数的（）内部需要指定构造对象需要的参数，也就是类构造函数的参数。</strong></p>
<h5 id="15-1-4-通过-reset-方法初始化"><a href="#15-1-4-通过-reset-方法初始化" class="headerlink" title="15.1.4 通过 reset 方法初始化"></a>15.1.4 通过 reset 方法初始化</h5><p>共享智能指针类提供的 std::shared_ptr::reset 方法函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Y &gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">( Y* ptr )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Y, <span class="keyword">class</span> Deleter &gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">( Y* ptr, Deleter d )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Y, <span class="keyword">class</span> Deleter, <span class="keyword">class</span> Alloc &gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">( Y* ptr, Deleter d, Alloc alloc )</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">ptr：指向要取得所有权的对象的指针</span></span><br><span class="line"><span class="comment">d：指向要取得所有权的对象的指针</span></span><br><span class="line"><span class="comment">aloc：内部存储所用的分配器</span></span><br></pre></td></tr></table></figure>

<p>测试代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1</span></span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr1 = <span class="built_in">make_shared</span>&lt;<span class="type">int</span>&gt;(<span class="number">520</span>);</span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr2 = ptr1;</span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr3 = ptr1;</span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr4 = ptr1;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr1管理的内存引用计数: &quot;</span> &lt;&lt; ptr1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr2管理的内存引用计数: &quot;</span> &lt;&lt; ptr2.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr3管理的内存引用计数: &quot;</span> &lt;&lt; ptr3.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr4管理的内存引用计数: &quot;</span> &lt;&lt; ptr4.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    ptr4.<span class="built_in">reset</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr1管理的内存引用计数: &quot;</span> &lt;&lt; ptr1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr2管理的内存引用计数: &quot;</span> &lt;&lt; ptr2.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr3管理的内存引用计数: &quot;</span> &lt;&lt; ptr3.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr4管理的内存引用计数: &quot;</span> &lt;&lt; ptr4.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr5;</span><br><span class="line">    ptr5.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">250</span>));</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ptr5管理的内存引用计数: &quot;</span> &lt;&lt; ptr5.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*输出：</span></span><br><span class="line"><span class="comment">ptr1管理的内存引用计数: 4</span></span><br><span class="line"><span class="comment">ptr2管理的内存引用计数: 4</span></span><br><span class="line"><span class="comment">ptr3管理的内存引用计数: 4</span></span><br><span class="line"><span class="comment">ptr4管理的内存引用计数: 4</span></span><br><span class="line"><span class="comment">    </span></span><br><span class="line"><span class="comment">ptr1管理的内存引用计数: 3</span></span><br><span class="line"><span class="comment">ptr2管理的内存引用计数: 3</span></span><br><span class="line"><span class="comment">ptr3管理的内存引用计数: 3</span></span><br><span class="line"><span class="comment">ptr4管理的内存引用计数: 0</span></span><br><span class="line"><span class="comment">    </span></span><br><span class="line"><span class="comment">ptr5管理的内存引用计数: 1</span></span><br></pre></td></tr></table></figure>

<p><strong>对于一个未初始化的共享智能指针，可以通过 reset 方法来初始化，当智能指针中有值的时候，调用 reset 会使引用计数减 1。</strong></p>
<h5 id="15-1-5-获取原始指针"><a href="#15-1-5-获取原始指针" class="headerlink" title="15.1.5 获取原始指针"></a>15.1.5 获取原始指针</h5><p>对应基础数据类型来说，通过操作智能指针和操作智能指针管理的内存效果是一样的，可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象，那么就需要取出原始内存的地址再操作，可以调用共享智能指针类提供的 get () 方法得到原始地址，其函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">T* <span class="title">get</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>测试代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> len = <span class="number">128</span>;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">char</span>&gt; <span class="title">ptr</span><span class="params">(<span class="keyword">new</span> <span class="type">char</span>[len])</span></span>;</span><br><span class="line">    <span class="comment">// 得到指针的原始地址</span></span><br><span class="line">    <span class="type">char</span>* add = ptr.<span class="built_in">get</span>();</span><br><span class="line">    <span class="built_in">memset</span>(add, <span class="number">0</span>, len);</span><br><span class="line">    <span class="built_in">strcpy</span>(add, <span class="string">&quot;我是要成为海贼王的男人!!!&quot;</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;string: &quot;</span> &lt;&lt; add &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">p</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>)</span></span>;</span><br><span class="line">    *p = <span class="number">100</span>;</span><br><span class="line">    cout &lt;&lt; *p.<span class="built_in">get</span>() &lt;&lt; <span class="string">&quot;  &quot;</span> &lt;&lt; *p &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*获取原始指针的两种方式</span></span><br><span class="line"><span class="comment">Test* t=ptr5.get();</span></span><br><span class="line"><span class="comment">t-&gt;setValue(1000);</span></span><br><span class="line"><span class="comment">t-&gt;print();</span></span><br><span class="line"><span class="comment">第二种</span></span><br><span class="line"><span class="comment">ptr5-&gt;setValue(999);</span></span><br><span class="line"><span class="comment">ptr-&gt;print();</span></span><br></pre></td></tr></table></figure>

<h4 id="15-2-指定删除器"><a href="#15-2-指定删除器" class="headerlink" title="15.2 指定删除器"></a>15.2 指定删除器</h4><p>当智能指针管理的内存对应的引用计数变为 0 的时候，这块内存就会被智能指针析构掉了。另外，我们在初始化智能指针的时候也可以自己指定删除动作，这个删除操作对应的函数被称之为删除器，这个删除器函数本质是一个回调函数，我们只需要进行实现，其调用是由智能指针完成的。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义删除器函数，释放int型内存</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">deleteIntPtr</span><span class="params">(<span class="type">int</span>* p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;int 型内存被释放了...&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">250</span>), deleteIntPtr)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>删除器函数也可以是 lambda 表达式，因此代码也可以写成下面这样：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">250</span>), [](<span class="type">int</span>* p) &#123;<span class="keyword">delete</span> p; &#125;)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//在上面的代码中，lambda表达式的参数就是智能指针管理的内存的地址，有了这个地址之后函数体内部就可以完成删除操作了。</span></span><br></pre></td></tr></table></figure>

<p>在 C++11 中使用 shared_ptr 管理动态数组时，需要指定删除器，因为 std::shared_ptr的默认删除器不支持数组对象，具体的处理代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>[<span class="number">10</span>], [](<span class="type">int</span>* p) &#123;<span class="keyword">delete</span>[]p; &#125;)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在删除数组内存时，除了自己编写删除器，也可以使用 C++ 提供的 std::default_delete<T>() 函数作为删除器，这个函数内部的删除功能也是通过调用 delete 来实现的，要释放什么类型的内存就将模板类型 T 指定为什么类型即可。具体处理代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>[<span class="number">10</span>], default_delete&lt;<span class="type">int</span>[]&gt;())</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;		</span><br></pre></td></tr></table></figure>

<p>另外，我们还可以自己封装一个 make_shared_array 方法来让 shared_ptr 支持数组，代码如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function">shared_ptr&lt;T&gt; <span class="title">make_share_array</span><span class="params">(<span class="type">size_t</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 返回匿名对象</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">shared_ptr</span>&lt;T&gt;(<span class="keyword">new</span> T[size], <span class="built_in">default_delete</span>&lt;T[]&gt;());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; ptr1 = <span class="built_in">make_share_array</span>&lt;<span class="type">int</span>&gt;(<span class="number">10</span>);</span><br><span class="line">    cout &lt;&lt; ptr1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    shared_ptr&lt;<span class="type">char</span>&gt; ptr2 = <span class="built_in">make_share_array</span>&lt;<span class="type">char</span>&gt;(<span class="number">128</span>);</span><br><span class="line">    cout &lt;&lt; ptr2.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="16-独占的智能指针"><a href="#16-独占的智能指针" class="headerlink" title="16. 独占的智能指针"></a>16. 独占的智能指针</h3><h4 id="16-1-初始化"><a href="#16-1-初始化" class="headerlink" title="16.1 初始化"></a>16.1 初始化</h4><p>std::unique_ptr 是一个独占型的智能指针，它不允许其他的智能指针共享其内部的指针，可以通过它的构造函数初始化一个独占智能指针对象，但是不允许通过赋值将一个 unique_ptr 赋值给另一个 unique_ptr。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 通过构造函数初始化对象</span></span><br><span class="line"><span class="function">unique_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>))</span></span>;</span><br><span class="line"><span class="comment">// error, 不允许将一个unique_ptr赋值给另一个unique_ptr</span></span><br><span class="line">unique_ptr&lt;<span class="type">int</span>&gt; ptr2 = ptr1;</span><br></pre></td></tr></table></figure>

<p>std::unique_ptr 不允许复制，但是可以通过函数返回给其他的 std::unique_ptr，还可以通过 std::move 来转译给其他的 std::unique_ptr，这样原始指针的所有权就被转移了，这个原始指针还是被独占的。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function">unique_ptr&lt;<span class="type">int</span>&gt; <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">unique_ptr</span>&lt;<span class="type">int</span>&gt;(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">520</span>)); <span class="comment">//即将要析构的临时对象</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 通过构造函数初始化</span></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>))</span></span>;</span><br><span class="line">    <span class="comment">// 通过转移所有权的方式初始化</span></span><br><span class="line">    unique_ptr&lt;<span class="type">int</span>&gt; ptr2 = <span class="built_in">move</span>(ptr1);</span><br><span class="line">    unique_ptr&lt;<span class="type">int</span>&gt; ptr3 = <span class="built_in">func</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>unique_ptr 独占智能指针类也有一个 reset 方法，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">( pointer ptr = pointer() )</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>使用 reset 方法可以让 unique_ptr 解除对原始内存的管理，也可以用来初始化一个独占的智能指针。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>))</span></span>;</span><br><span class="line">    unique_ptr&lt;<span class="type">int</span>&gt; ptr2 = <span class="built_in">move</span>(ptr1);</span><br><span class="line"></span><br><span class="line">    ptr1.<span class="built_in">reset</span>();  <span class="comment">// 解除对原始内存的管理</span></span><br><span class="line">    ptr2.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">250</span>));  <span class="comment">//重新指定智能指针管理的原始内存</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果想要获取独占智能指针管理的原始地址，可以调用 get () 方法，函数原型如下：<code> pointer get() const noexcept;</code></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">示例：</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>))</span></span>;</span><br><span class="line">    unique_ptr&lt;<span class="type">int</span>&gt; ptr2 = <span class="built_in">move</span>(ptr1);</span><br><span class="line"></span><br><span class="line">    ptr2.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">250</span>));</span><br><span class="line">    cout &lt;&lt; *ptr2.<span class="built_in">get</span>() &lt;&lt; endl;	<span class="comment">// 得到内存地址中存储的实际数值 250</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="16-2-删除器"><a href="#16-2-删除器" class="headerlink" title="16.2 删除器"></a>16.2 删除器</h4><p>unique_ptr 指定删除器和 shared_ptr 指定删除器是有区别的，<strong>unique_ptr 指定删除器的时候需要确定删除器的类型</strong>，所以不能像 shared_ptr 那样直接指定删除器，举例说明：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>), [](<span class="type">int</span>*p) &#123;<span class="keyword">delete</span> p; &#125;)</span></span>;	<span class="comment">// ok</span></span><br><span class="line"><span class="function">unique_ptr&lt;<span class="type">int</span>&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>), [](<span class="type">int</span>*p) &#123;<span class="keyword">delete</span> p; &#125;)</span></span>;	<span class="comment">// error 没有指定确定删除器的类型</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">using</span> func_ptr = <span class="built_in">void</span>(*)(<span class="type">int</span>*);</span><br><span class="line">  <span class="number">7</span>  <span class="function">unique_ptr&lt;<span class="type">int</span>, func_ptr&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>), [](<span class="type">int</span>*p) &#123;<span class="keyword">delete</span> p; &#125;)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的代码中第 7 行，func_ptr 的类型和 lambda表达式的类型是一致的。在 lambda 表达式<strong>没有捕获任何变量的情况下是正确的</strong>，如果捕获了变量，编译时则会报错：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">using</span> func_ptr = <span class="built_in">void</span>(*)(<span class="type">int</span>*);</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="type">int</span>, func_ptr&gt; <span class="title">ptr1</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>), [&amp;](<span class="type">int</span>*p) &#123;<span class="keyword">delete</span> p; &#125;)</span></span>;	<span class="comment">// error</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的代码中错误原因是这样的，在 lambda 表达式没有捕获任何外部变量时，可以直接转换为函数指针，<strong>一旦捕获了就无法转换了，会把它看做是仿函数</strong>，如果想要让编译器成功通过编译，那么需要使用可调用对象包装器来处理声明的函数指针：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">using</span> func_ptr = <span class="built_in">void</span>(*)(<span class="type">int</span>*);</span><br><span class="line">    unique_ptr&lt;<span class="type">int</span>, function&lt;<span class="type">void</span>(<span class="type">int</span>*)&gt;&gt; <span class="built_in">ptr1</span>(<span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">10</span>), [&amp;](<span class="type">int</span>*p) &#123;<span class="keyword">delete</span> p; &#125;);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//function&lt;void(int*) p=[&amp;](int*p) &#123;delete p; &#125;</span></span><br><span class="line"><span class="comment">//独占的智能指针可以管理数组类型的地址，能够自动释放</span></span><br><span class="line"><span class="function">unique_ptr&lt;Test[]&gt; <span class="title">ptr5</span><span class="params">(<span class="keyword">new</span> Test[<span class="number">3</span>])</span></span>;</span><br><span class="line"><span class="comment">//在c++11中share_ptr不支持下面的写法，之后的版本才支持</span></span><br><span class="line"><span class="function">shared_ptr&lt;Test[]&gt; <span class="title">ptr6</span><span class="params">(<span class="keyword">new</span> Test[<span class="number">3</span>])</span></span>;</span><br></pre></td></tr></table></figure>

<h3 id="17-弱引用智能指针"><a href="#17-弱引用智能指针" class="headerlink" title="17. 弱引用智能指针"></a>17. 弱引用智能指针</h3><h4 id="17-1-基本使用方法"><a href="#17-1-基本使用方法" class="headerlink" title="17.1 基本使用方法"></a>17.1 基本使用方法</h4><p>弱引用智能指针 std::weak_ptr 可以看做是 shared_ptr 的助手，它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符 * 和 -&gt;，因为它不共享指针，不能操作资源，所以它的构造不会增加引用计数，析构也不会减少引用计数，<strong>它的主要作用就是作为一个旁观者监视 shared_ptr 中管理的资源是否存在。</strong></p>
<h5 id="17-1-1-初始化"><a href="#17-1-1-初始化" class="headerlink" title="17.1.1 初始化"></a><strong>17.1.1 初始化</strong></h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 默认构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="title">weak_ptr</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="comment">// 拷贝构造</span></span><br><span class="line"><span class="built_in">weak_ptr</span> (<span class="type">const</span> weak_ptr&amp; x) <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">U</span>&gt; <span class="built_in">weak_ptr</span> (<span class="type">const</span> weak_ptr&lt;U&gt;&amp; x) <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// 通过shared_ptr对象构造</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">U</span>&gt; <span class="built_in">weak_ptr</span> (<span class="type">const</span> shared_ptr&lt;U&gt;&amp; x) <span class="keyword">noexcept</span>;</span><br></pre></td></tr></table></figure>

<p>在 C++11 中，weak_ptr 的初始化可以通过以上提供的构造函数来完成初始化，具体使用方法如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp1;     <span class="comment">//构造了一个空 weak_ptr 对象</span></span><br><span class="line">    <span class="function">weak_ptr&lt;<span class="type">int</span>&gt; <span class="title">wp2</span><span class="params">(wp1)</span></span>;  <span class="comment">//通过一个空 weak_ptr 对象构造了另一个空 weak_ptr 对象</span></span><br><span class="line">    <span class="function">weak_ptr&lt;<span class="type">int</span>&gt; <span class="title">wp3</span><span class="params">(sp)</span></span>;   <span class="comment">// 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象</span></span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp4;       </span><br><span class="line">    wp4 = sp;              <span class="comment">//通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象（这是一个隐式类型转换）</span></span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp5;</span><br><span class="line">    wp5 = wp3;           <span class="comment">//通过一个 weak_ptr 对象构造了一个可用的 weak_ptr 实例对象   </span></span><br><span class="line">     </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="17-2-其他使用方法"><a href="#17-2-其他使用方法" class="headerlink" title="17.2 其他使用方法"></a>17.2 其他使用方法</h4><h5 id="17-2-1-use-count"><a href="#17-2-1-use-count" class="headerlink" title="17.2.1 use_count()"></a>17.2.1 use_count()</h5><p>通过调用 std::weak_ptr 类提供的 use_count() 方法可以获得当前所观测资源的引用计数，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 函数返回所监测的资源的引用计数</span></span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">int</span> <span class="title">use_count</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>修改一下上面的测试程序，添加打印资源引用计数的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp1;</span><br><span class="line">    <span class="function">weak_ptr&lt;<span class="type">int</span>&gt; <span class="title">wp2</span><span class="params">(wp1)</span></span>;</span><br><span class="line">    <span class="function">weak_ptr&lt;<span class="type">int</span>&gt; <span class="title">wp3</span><span class="params">(sp)</span></span>;</span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp4;</span><br><span class="line">    wp4 = sp;</span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp5;</span><br><span class="line">    wp5 = wp3;</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;wp1: &quot;</span> &lt;&lt; wp1.<span class="built_in">use_count</span>() &lt;&lt; endl;     <span class="comment">//0</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;wp2: &quot;</span> &lt;&lt; wp2.<span class="built_in">use_count</span>() &lt;&lt; endl;   <span class="comment">//0</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;wp3: &quot;</span> &lt;&lt; wp3.<span class="built_in">use_count</span>() &lt;&lt; endl;   <span class="comment">//1</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;wp4: &quot;</span> &lt;&lt; wp4.<span class="built_in">use_count</span>() &lt;&lt; endl;   <span class="comment">//1</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;wp5: &quot;</span> &lt;&lt; wp5.<span class="built_in">use_count</span>() &lt;&lt; endl;   <span class="comment">//1</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过打印的结果可以知道，虽然弱引用智能指针 wp3、wp4、wp5 监测的资源是同一个，但是它的引用计数并没有发生任何的变化，也进一步证明了 weak_ptr只是监测资源，并不管理资源。</p>
<h5 id="17-2-2-expired"><a href="#17-2-2-expired" class="headerlink" title="17.2.2 expired()"></a>17.2.2 expired()</h5><p>通过调用 std::weak_ptr 类提供的 expired() 方法来判断观测的资源是否已经被释放，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回true表示资源已经被释放, 返回false表示资源没有被释放</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">expired</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">shared</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>))</span></span>;</span><br><span class="line">    <span class="function">weak_ptr&lt;<span class="type">int</span>&gt; <span class="title">weak</span><span class="params">(shared)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;1. weak &quot;</span> &lt;&lt; (weak.<span class="built_in">expired</span>() ? <span class="string">&quot;is&quot;</span> : <span class="string">&quot;is not&quot;</span>) &lt;&lt; <span class="string">&quot; expired&quot;</span> &lt;&lt; endl; <span class="comment">//1. weak is not expired</span></span><br><span class="line"></span><br><span class="line">    shared.<span class="built_in">reset</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;2. weak &quot;</span> &lt;&lt; (weak.<span class="built_in">expired</span>() ? <span class="string">&quot;is&quot;</span> : <span class="string">&quot;is not&quot;</span>) &lt;&lt; <span class="string">&quot; expired&quot;</span> &lt;&lt; endl;<span class="comment">//2. weak is expired</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>weak_ptr 监测的就是 shared_ptr 管理的资源，当共享智能指针调用 shared.reset(); 之后管理的资源被释放，因此 weak.expired() 函数的结果返回 true，表示监测的资源已经不存在了。</p>
<h5 id="17-2-3-lock"><a href="#17-2-3-lock" class="headerlink" title="17.2.3 lock()"></a>17.2.3 lock()</h5><p>通过调用 std::weak_ptr 类提供的 lock() 方法来获取管理<strong>所监测资源的 shared_ptr 对象</strong>，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">shared_ptr&lt;element_type&gt; <span class="title">lock</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>函数的使用方法如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    shared_ptr&lt;<span class="type">int</span>&gt; sp1, sp2;</span><br><span class="line">    weak_ptr&lt;<span class="type">int</span>&gt; wp;</span><br><span class="line"></span><br><span class="line">    sp1 = std::<span class="built_in">make_shared</span>&lt;<span class="type">int</span>&gt;(<span class="number">520</span>);</span><br><span class="line">    wp = sp1;</span><br><span class="line">    sp2 = wp.<span class="built_in">lock</span>();  <span class="comment">//获得一个wp的监测对象的共享指针对象</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; wp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    sp1.<span class="built_in">reset</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; wp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    sp1 = wp.<span class="built_in">lock</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; wp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;*sp1: &quot;</span> &lt;&lt; *sp1 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;*sp2: &quot;</span> &lt;&lt; *sp2 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1. sp2 = wp.lock(); 通过调用 lock() 方法得到一个用于管理 weak_ptr 对象所监测的资源的共享智能指针对象，</span></span><br><span class="line"><span class="comment">   使用这个对象初始化 sp2，此时所监测资源的引用计数为 2</span></span><br><span class="line"><span class="comment">2. sp1.reset(); 共享智能指针 sp1 被重置，weak_ptr 对象所监测的资源的引用计数减 1</span></span><br><span class="line"><span class="comment">3. sp1 = wp.lock();sp1 重新被初始化，并且管理的还是 weak_ptr 对象所监测的资源，因此引用计数加 1</span></span><br><span class="line"><span class="comment">   共享智能指针对象 sp1 和 sp2 管理的是同一块内存，因此最终打印的内存中的结果是相同的，都是 520</span></span><br></pre></td></tr></table></figure>

<h5 id="17-2-4-reset"><a href="#17-2-4-reset" class="headerlink" title="17.2.4 reset()"></a>17.2.4 reset()</h5><p>通过调用 std::weak_ptr 类提供的 reset() 方法来清空对象，使其不监测任何资源，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>函数的使用是非常简单的，示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="type">int</span>&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> <span class="type">int</span>(<span class="number">10</span>))</span></span>;</span><br><span class="line">    <span class="function">weak_ptr&lt;<span class="type">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;1. wp &quot;</span> &lt;&lt; (wp.<span class="built_in">expired</span>() ? <span class="string">&quot;is&quot;</span> : <span class="string">&quot;is not&quot;</span>) &lt;&lt; <span class="string">&quot; expired&quot;</span> &lt;&lt; endl; <span class="comment">//is not</span></span><br><span class="line"></span><br><span class="line">    wp.<span class="built_in">reset</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;2. wp &quot;</span> &lt;&lt; (wp.<span class="built_in">expired</span>() ? <span class="string">&quot;is&quot;</span> : <span class="string">&quot;is not&quot;</span>) &lt;&lt; <span class="string">&quot; expired&quot;</span> &lt;&lt; endl;  <span class="comment">//is</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//weak_ptr 对象 sp 被重置之后 wp.reset(); 变成了空对象，不再监测任何资源，因此 wp.expired() 返回 true</span></span><br></pre></td></tr></table></figure>

<h4 id="17-3-返回管理this的share-ptr"><a href="#17-3-返回管理this的share-ptr" class="headerlink" title="17.3 返回管理this的share_ptr"></a>17.3 返回管理this的share_ptr</h4><p>如果在一个类中编写了一个函数，通过这个得到管理当前对象的共享智能指针，我们可能会写出如下代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;Test&gt; <span class="title">getSharedPtr</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">shared_ptr</span>&lt;Test&gt;(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    ~<span class="built_in">Test</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;class Test is disstruct ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;Test&gt; <span class="title">sp1</span><span class="params">(<span class="keyword">new</span> Test)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; sp1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    shared_ptr&lt;Test&gt; sp2 = sp1-&gt;<span class="built_in">getSharedPtr</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; sp1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*执行上面的测试代码，运行中会出现异常，在终端还是能看到对应的日志输出：</span></span><br><span class="line"><span class="comment">use_count: 1</span></span><br><span class="line"><span class="comment">use_count: 1</span></span><br><span class="line"><span class="comment">class Test is disstruct ...</span></span><br><span class="line"><span class="comment">class Test is disstruct ...</span></span><br></pre></td></tr></table></figure>

<p>通过输出的结果可以看到一个对象被析构了两次，其原因是这样的：在这个例子中使用同一个指针 this 构造了两个智能指针对象 sp1 和 sp2，这二者之间是没有任何关系的，因为 sp2 并不是通过 sp1 初始化得到的实例对象。在离开作用域之后 this 将被构造的两个智能指针各自析构，导致重复析构的错误。</p>
<p>这个问题可以通过 weak_ptr 来解决，通过 wek_ptr 返回管理 this 资源的共享智能指针对象 shared_ptr。C++11 中为我们提供了一个模板类叫做 **std::enable_shared_from_this<T>**，这个类中有一个方法叫做 <strong>shared_from_this()<strong>，通过这个方法可以返回一个共享智能指针，</strong>在函数的内部就是使用 weak_ptr 来监测 this 对象，并通过调用 weak_ptr 的 lock() 方法返回一个 shared_ptr 对象。</strong></p>
<p>修改之后的代码为：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span> : <span class="keyword">public</span> enable_shared_from_this&lt;Test&gt;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;Test&gt; <span class="title">getSharedPtr</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">shared_from_this</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    ~<span class="built_in">Test</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;class Test is disstruct ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;Test&gt; <span class="title">sp1</span><span class="params">(<span class="keyword">new</span> Test)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; sp1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    shared_ptr&lt;Test&gt; sp2 = sp1-&gt;<span class="built_in">getSharedPtr</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;use_count: &quot;</span> &lt;&lt; sp1.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最后需要强调一个细节：在调用 enable_shared_from_this 类的 shared_from_this () 方法之前，必须要先初始化函数内部 weak_ptr 对象，否则该函数无法返回一个有效的 shared_ptr 对象（具体处理方法可以参考上面的示例代码）。</p>
<h4 id="17-4-解决循环引用问题"><a href="#17-4-解决循环引用问题" class="headerlink" title="17.4 解决循环引用问题"></a>17.4 解决循环引用问题</h4><p>智能指针如果循环引用会导致内存泄露，比如下面的例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TA</span>;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TB</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TA</span></span><br><span class="line">&#123;</span><br><span class="line">    shared_ptr&lt;TB&gt; bptr;</span><br><span class="line">    ~<span class="built_in">TA</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;class TA is disstruct ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TB</span></span><br><span class="line">&#123;</span><br><span class="line">    shared_ptr&lt;TA&gt; aptr;</span><br><span class="line">    ~<span class="built_in">TB</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;class TB is disstruct ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">testPtr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;TA&gt; <span class="title">ap</span><span class="params">(<span class="keyword">new</span> TA)</span></span>;</span><br><span class="line">    <span class="function">shared_ptr&lt;TB&gt; <span class="title">bp</span><span class="params">(<span class="keyword">new</span> TB)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TA object use_count: &quot;</span> &lt;&lt; ap.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TB object use_count: &quot;</span> &lt;&lt; bp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    ap-&gt;bptr = bp;</span><br><span class="line">    bp-&gt;aptr = ap;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TA object use_count: &quot;</span> &lt;&lt; ap.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TB object use_count: &quot;</span> &lt;&lt; bp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">testPtr</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在测试程序中，共享智能指针 ap、bp 对 TA、TB 实例对象的引用计数变为 2，在共享智能指针离开作用域之后引用计数只能减为1，这种情况下不会去删除智能指针管理的内存，导致类 TA、TB 的实例对象不能被析构，最终造成内存泄露。通过使用 weak_ptr 可以解决这个问题，只要将类 TA 或者 TB 的任意一个成员改为 weak_ptr，修改之后的代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TA</span>;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TB</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TA</span></span><br><span class="line">&#123;</span><br><span class="line">    weak_ptr&lt;TB&gt; bptr;      <span class="comment">//将两个中任意一个或者都改为弱引用智能指针都是可以的</span></span><br><span class="line">    ~<span class="built_in">TA</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;class TA is disstruct ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">TB</span></span><br><span class="line">&#123;</span><br><span class="line">    shared_ptr&lt;TA&gt; aptr;</span><br><span class="line">    ~<span class="built_in">TB</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;class TB is disstruct ...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">testPtr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;TA&gt; <span class="title">ap</span><span class="params">(<span class="keyword">new</span> TA)</span></span>;</span><br><span class="line">    <span class="function">shared_ptr&lt;TB&gt; <span class="title">bp</span><span class="params">(<span class="keyword">new</span> TB)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TA object use_count: &quot;</span> &lt;&lt; ap.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TB object use_count: &quot;</span> &lt;&lt; bp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    ap-&gt;bptr = bp;</span><br><span class="line">    bp-&gt;aptr = ap;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TA object use_count: &quot;</span> &lt;&lt; ap.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;TB object use_count: &quot;</span> &lt;&lt; bp.<span class="built_in">use_count</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">testPtr</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*通过输出的结果可以看到类 TA 或者 TB 的对象被成功析构了。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">上面程序中，在对类 TA 成员赋值时 ap-&gt;bptr = bp; 由于 bptr 是 weak_ptr 类型，这个赋值操作并不会增加引用计数，所以 bp 的引用计数仍然为 1，在离开作用域之后 bp 的引用计数减为 0，类 TB 的实例对象被析构。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">在类 TB 的实例对象被析构的时候，内部的 aptr 也被析构，其对 TA 对象的管理解除，内存的引用计数减为 1，当共享智能指针 ap 离开作用域之后，对 TA 对象的管理也解除了，内存的引用计数减为 0，类 TA 的实例对象被析构。</span></span><br><span class="line"><span class="comment"></span></span><br></pre></td></tr></table></figure>

<h2 id="18-多线程"><a href="#18-多线程" class="headerlink" title="18. 多线程"></a>18. 多线程</h2><h3 id="18-1-处理日期和时间的-chrono-库"><a href="#18-1-处理日期和时间的-chrono-库" class="headerlink" title="18.1 处理日期和时间的 chrono 库"></a>18.1 处理日期和时间的 chrono 库</h3><p>C++11 中提供了日期和时间相关的库 chrono，通过 chrono 库可以很方便地处理日期和时间，为程序的开发提供了便利。chrono 库主要包含三种类型的类：<strong>时间间隔duration、时钟clocks、时间点time point。</strong></p>
<h4 id="18-1-1-时间间隔duration"><a href="#18-1-1-时间间隔duration" class="headerlink" title="18.1.1 时间间隔duration"></a>18.1.1 时间间隔duration</h4><h5 id="1-1-常用类成员"><a href="#1-1-常用类成员" class="headerlink" title="1.1 常用类成员"></a>1.1 常用类成员</h5><p>duration表示一段时间间隔，用来记录时间长度，可以表示几秒、几分钟、几个小时的时间间隔。duration 的原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;chrono&gt;</span></span><br><span class="line"><span class="keyword">template</span>&lt;</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Rep</span>,</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Period</span> = std::ratio&lt;<span class="number">1</span>&gt;</span><br><span class="line">&gt; <span class="keyword">class</span> duration;</span><br></pre></td></tr></table></figure>

<p>Period：表示时钟的周期，它的原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;ratio&gt;</span></span><br><span class="line"><span class="keyword">template</span>&lt;</span><br><span class="line">    std::<span class="type">intmax_t</span> Num,</span><br><span class="line">    std::<span class="type">intmax_t</span> Denom = <span class="number">1</span></span><br><span class="line">&gt; <span class="keyword">class</span> ratio;</span><br></pre></td></tr></table></figure>

<p>ratio 类表示每个时钟周期的秒数，其中第一个模板参数 Num代表分子，Denom代表分母，该分母值默认为 1，因此，ratio代表的是一个分子除以分母的数值，比如：ratio&lt;2&gt; 代表一个时钟周期是 2 秒，ratio&lt;60 &gt; 代表一分钟，ratio&lt;60*60 &gt; 代表一个小时，ratio&lt;60*60*24 &gt; 代表一天。而 ratio&lt;1,1000 &gt; 代表的是 1&#x2F;1000 秒，也就是 1 毫秒，ratio&lt;1,1000000 &gt; 代表一微秒，ratio&lt;1,1000000000 &gt; 代表一纳秒。</p>
<p>为了方便使用，在标准库中定义了一些常用的时间间隔，比如：时、分、秒、毫秒、微秒、纳秒，它们都位于 chrono 命名空间下，定义如下：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>定义</th>
</tr>
</thead>
<tbody><tr>
<td>纳秒：std::chrono::nanoseconds</td>
<td>duration&lt;Rep*&#x2F;* 至少 64 位的有符号整数类型 *&#x2F;*, std::nano&gt;</td>
</tr>
<tr>
<td>微秒：std::chrono::microseconds</td>
<td>duration&lt;Rep*&#x2F;* 至少 55 位的有符号整数类型 *&#x2F;*, std::micro&gt;</td>
</tr>
<tr>
<td>毫秒：std::chrono::milliseconds</td>
<td>duration&lt;Rep*&#x2F;* 至少 45 位的有符号整数类型 *&#x2F;*, std::milli&gt;</td>
</tr>
<tr>
<td>秒： std::chrono::seconds</td>
<td>duration&lt;Rep*&#x2F;* 至少 35 位的有符号整数类型 *&#x2F;*&gt;</td>
</tr>
<tr>
<td>分钟：std::chrono::minutes</td>
<td>duration&lt;Rep*&#x2F;* 至少 29 位的有符号整数类型 *&#x2F;*, std::ratio&lt;60&gt;&gt;</td>
</tr>
<tr>
<td>小时：std::chrono::hours</td>
<td>duration&lt;Rep*&#x2F;* 至少 23 位的有符号整数类型 *&#x2F;*, std::ratio&lt;3600&gt;&gt;</td>
</tr>
</tbody></table>
<p>​	duration 类的构造函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 拷贝构造函数</span></span><br><span class="line"><span class="built_in">duration</span>( <span class="type">const</span> duration&amp; ) = <span class="keyword">default</span>;</span><br><span class="line"><span class="comment">// 2. 通过指定时钟周期的类型来构造对象</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Rep2 &gt;</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="keyword">explicit</span> <span class="title">duration</span><span class="params">( <span class="type">const</span> Rep2&amp; r )</span></span>;</span><br><span class="line"><span class="comment">// 3. 通过指定时钟周期类型，和时钟周期长度来构造对象</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Rep2, <span class="keyword">class</span> Period2 &gt;</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="title">duration</span><span class="params">( <span class="type">const</span> duration&lt;Rep2,Period2&gt;&amp; d )</span></span>;</span><br></pre></td></tr></table></figure>

<p>duration 类还提供了获取时间间隔的时钟周期数的方法 count ()，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">constexpr</span> rep <span class="title">count</span><span class="params">()</span> <span class="type">const</span></span>;</span><br></pre></td></tr></table></figure>

<h5 id="1-2-类的使用"><a href="#1-2-类的使用" class="headerlink" title="1.2 类的使用"></a>1.2 类的使用</h5><p>通过构造函数构造事件间隔对象示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">chrono::hours <span class="title">h</span><span class="params">(<span class="number">1</span>)</span></span>;                          <span class="comment">// 一小时</span></span><br><span class="line">    chrono::milliseconds ms&#123; <span class="number">3</span> &#125;;                <span class="comment">// 3 毫秒</span></span><br><span class="line">    chrono::duration&lt;<span class="type">int</span>, ratio&lt;<span class="number">1000</span>&gt;&gt; <span class="built_in">ks</span>(<span class="number">3</span>);    <span class="comment">// 3000 秒</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// chrono::duration&lt;int, ratio&lt;1000&gt;&gt; d3(3.5);  // error</span></span><br><span class="line">    <span class="function">chrono::duration&lt;<span class="type">double</span>&gt; <span class="title">dd</span><span class="params">(<span class="number">6.6</span>)</span></span>;               <span class="comment">// 6.6 秒</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用小数表示时钟周期的次数</span></span><br><span class="line">    chrono::duration&lt;<span class="type">double</span>, std::ratio&lt;<span class="number">1</span>, <span class="number">30</span>&gt;&gt; <span class="built_in">hz</span>(<span class="number">3.5</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>chrono 库中根据 duration 类封装了不同长度的时钟周期（也可以自定义），基于这个时钟周期再进行周期次数的设置就可以得到总的时间间隔了（时钟周期 * 周期次数 &#x3D; 总的时间间隔）。</p>
<p>​	示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::chrono::milliseconds ms&#123;<span class="number">3</span>&#125;;         <span class="comment">// 3 毫秒</span></span><br><span class="line">    std::chrono::microseconds us = <span class="number">2</span>*ms;     <span class="comment">// 6000 微秒</span></span><br><span class="line">    <span class="comment">// 时间间隔周期为 1/30 秒</span></span><br><span class="line">    std::chrono::duration&lt;<span class="type">double</span>, std::ratio&lt;<span class="number">1</span>, <span class="number">30</span>&gt;&gt; <span class="built_in">hz</span>(<span class="number">3.5</span>);</span><br><span class="line"> </span><br><span class="line">    std::cout &lt;&lt;  <span class="string">&quot;3 ms duration has &quot;</span> &lt;&lt; ms.<span class="built_in">count</span>() &lt;&lt; <span class="string">&quot; ticks\n&quot;</span></span><br><span class="line">              &lt;&lt;  <span class="string">&quot;6000 us duration has &quot;</span> &lt;&lt; us.<span class="built_in">count</span>() &lt;&lt; <span class="string">&quot; ticks\n&quot;</span></span><br><span class="line">              &lt;&lt;  <span class="string">&quot;3.5 hz duration has &quot;</span> &lt;&lt; hz.<span class="built_in">count</span>() &lt;&lt; <span class="string">&quot; ticks\n&quot;</span>;       </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由于在 duration 类内部做了操作符重载，因此时间间隔之间可以直接进行算术运算，比如我们要计算两个时间间隔的差值，就可以在代码中做如下处理：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">chrono::minutes <span class="title">t1</span><span class="params">(<span class="number">10</span>)</span></span>;</span><br><span class="line">    <span class="function">chrono::seconds <span class="title">t2</span><span class="params">(<span class="number">60</span>)</span></span>;</span><br><span class="line">    chrono::seconds t3 = t1 - t2;</span><br><span class="line">    cout &lt;&lt; t3.<span class="built_in">count</span>() &lt;&lt; <span class="string">&quot; second&quot;</span> &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意事项：duration 的加减运算有一定的规则，当两个 duration 时钟周期不相同的时候，会先统一成一种时钟，然后再进行算术运算，统一的规则如下：假设有 ratio&lt;x1,y1&gt; 和 ratio&lt;x2,y2 &gt; 两个时钟周期，首先需要求出 x1，x2 的最大公约数 X，然后求出 y1，y2 的最小公倍数 Y，统一之后的时钟周期 ratio 为 ratio&lt;X,Y&gt;。</p>
<h4 id="18-1-2-时间点-time-point"><a href="#18-1-2-时间点-time-point" class="headerlink" title="18.1.2 时间点 time point"></a>18.1.2 时间点 time point</h4><p>chrono 库中提供了一个表示时间点的类 time_point，该类的定义如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;chrono&gt;</span></span><br><span class="line"><span class="keyword">template</span>&lt;</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Clock</span>,</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Duration</span> = <span class="keyword">typename</span> Clock::duration</span><br><span class="line">&gt; <span class="keyword">class</span> time_point;</span><br></pre></td></tr></table></figure>

<p>time_point 类的构造函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 构造一个以新纪元(epoch，即：1970.1.1)作为值的对象，需要和时钟类一起使用，不能单独使用该无参构造函数</span></span><br><span class="line"><span class="built_in">time_point</span>();</span><br><span class="line"><span class="comment">// 2. 构造一个对象，表示一个时间点，其中d的持续时间从epoch开始，需要和时钟类一起使用，不能单独使用该构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">time_point</span><span class="params">( <span class="type">const</span> duration&amp; d )</span></span>;</span><br><span class="line"><span class="comment">// 3. 拷贝构造函数，构造与t相同时间点的对象，使用的时候需要指定模板参数</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Duration2 &gt;</span></span><br><span class="line"><span class="function"><span class="title">time_point</span><span class="params">( <span class="type">const</span> time_point&lt;Clock,Duration2&gt;&amp; t )</span></span>;</span><br></pre></td></tr></table></figure>

<h4 id="18-1-3-时钟clock"><a href="#18-1-3-时钟clock" class="headerlink" title="18.1.3 时钟clock"></a>18.1.3 时钟clock</h4><p>chrono 库中提供了获取当前的系统时间的时钟类，包含的时钟一共有三种：</p>
<ul>
<li>system_clock：系统的时钟，系统的时钟可以修改，甚至可以网络对时，因此使用系统时间计算时间差可能不准。</li>
<li>steady_clock：是固定的时钟，相当于秒表。开始计时后，时间只会增长并且不能修改，适合用于记录程序耗时</li>
<li>high_resolution_clock：和时钟类 steady_clock 是等价的（是它的别名）。</li>
</ul>
<p>在这些时钟类的内部有 time_point、duration、Rep、Period 等信息，基于这些信息来获取当前时间，以及实现 time_t 和 time_point 之间的相互转换。</p>
<p>1.另外还可以看到 system_clock 类一共提供了三个静态成员函数：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回表示当前时间的时间点。</span></span><br><span class="line"><span class="type">static</span> std::<span class="function">chrono::time_point&lt;std::chrono::system_clock&gt; <span class="title">now</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="comment">// 将 time_point 时间点类型转换为 std::time_t 类型</span></span><br><span class="line"><span class="function"><span class="type">static</span> std::<span class="type">time_t</span> <span class="title">to_time_t</span><span class="params">( <span class="type">const</span> time_point&amp; t )</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="comment">// 将 std::time_t 类型转换为 time_point 时间点类型</span></span><br><span class="line"><span class="type">static</span> std::chrono::<span class="function">system_clock::time_point <span class="title">from_time_t</span><span class="params">( std::<span class="type">time_t</span> t )</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 系统当前时间</span></span><br><span class="line">system_clock::time_point today = system_clock::<span class="built_in">now</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 转换为time_t时间类型</span></span><br><span class="line"><span class="type">time_t</span> tm = system_clock::<span class="built_in">to_time_t</span>(today);</span><br><span class="line">cout &lt;&lt; <span class="string">&quot;今天的日期是:    &quot;</span> &lt;&lt; <span class="built_in">ctime</span>(&amp;tm);</span><br></pre></td></tr></table></figure>

<p>2.另外，在这个steady_clock类中也提供了一个静态的 now () 方法，用于得到当前的时间点，函数原型如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">static std::chrono::time_point&lt;std::chrono::steady_clock&gt; now() noexcept;</span><br></pre></td></tr></table></figure>

<p>假设要测试某一段程序的执行效率，可以计算它执行期间消耗的总时长，示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std::chrono;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 获取开始时间点</span></span><br><span class="line">    steady_clock::time_point start = steady_clock::<span class="built_in">now</span>();</span><br><span class="line">    <span class="comment">// 执行业务流程</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;print 1000 stars ....&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;*&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 获取结束时间点</span></span><br><span class="line">    steady_clock::time_point last = steady_clock::<span class="built_in">now</span>();</span><br><span class="line">    <span class="comment">// 计算差值</span></span><br><span class="line">    <span class="keyword">auto</span> dt = last - start;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;总共耗时: &quot;</span> &lt;&lt; dt.<span class="built_in">count</span>() &lt;&lt; <span class="string">&quot;纳秒&quot;</span> &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>3.high_resolution_clock 提供的时钟精度比 system_clock 要高，它也是不可以修改的。在底层源码中，这个类其实是 steady_clock 类的别名。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> high_resolution_clock = steady_clock;</span><br></pre></td></tr></table></figure>

<h3 id="18-2-cop-C-线程类-thread"><a href="#18-2-cop-C-线程类-thread" class="headerlink" title="18.2 :cop:C++ 线程类 thread"></a>18.2 :cop:C++ 线程类 thread</h3><p>头文件：#include<thread></p>
<p>线程类：std::thread</p>
<p>C++11 中提供的线程类叫做 std::thread，基于这个类创建一个新的线程非常的简单，只需要提供线程函数或者函数对象即可，并且可以同时指定线程函数的参数。我们首先来了解一下这个类提供的一些常用 API：</p>
<h4 id="18-2-1构造函数"><a href="#18-2-1构造函数" class="headerlink" title="18.2.1构造函数:"></a>18.2.1<strong>构造函数:</strong></h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ①</span></span><br><span class="line"><span class="built_in">thread</span>() <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// ②</span></span><br><span class="line"><span class="built_in">thread</span>( thread&amp;&amp; other ) <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// ③最常用</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Function, <span class="keyword">class</span>... Args &gt;</span></span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">thread</span><span class="params">( Function&amp;&amp; f, Args&amp;&amp;... args )</span></span>;</span><br><span class="line"><span class="comment">// ④</span></span><br><span class="line"><span class="built_in">thread</span>( <span class="type">const</span> thread&amp; ) = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>

<p>构造函数①：默认构造函，构造一个线程对象，在这个线程中不执行任何处理动作</p>
<p>构造函数②：移动构造函数，将 other 的线程所有权转移给新的 thread 对象。之后 other 不再表示执行线程。</p>
<p>构造函数③：创建线程对象，并在该线程中执行函数 f 中的业务逻辑，args 是要传递给函数 f 的参数</p>
<ul>
<li>任务函数 f 的可选类型有很多，具体如下：<ul>
<li>普通函数，类成员函数，匿名函数，仿函数（这些都是可调用对象类型）</li>
<li>可以是可调用对象包装器类型，也可以是使用绑定器绑定之后得到的类型（仿函数）</li>
</ul>
</li>
</ul>
<p>构造函数④：使用 &#x3D;delete 显示删除拷贝构造，不允许线程对象之间的拷贝</p>
<p>示例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;windows.h&gt;</span> <span class="comment">//Sleep()函数需要这个头文件</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> bh,<span class="type">const</span> string&amp;str)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;第&quot;</span>&lt;&lt;i&lt;&lt;<span class="string">&quot;次表白&quot;</span>&lt;&lt;num&lt;&lt;<span class="string">&quot;号&quot;</span>&lt;&lt;str&lt;&lt;endl;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">1000</span>);<span class="comment">//休眠1秒</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="comment">//1.用普通函数创建线程</span></span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func,<span class="number">3</span>,<span class="string">&quot;妲己&quot;</span>)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//2.用lambda函数创建线程</span></span><br><span class="line">    <span class="keyword">auto</span> f=[](<span class="type">int</span> bh,<span class="type">const</span> string&amp;str)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;第&quot;</span>&lt;&lt;i&lt;&lt;<span class="string">&quot;次表白&quot;</span>&lt;&lt;num&lt;&lt;<span class="string">&quot;号&quot;</span>&lt;&lt;str&lt;&lt;endl;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">1000</span>);<span class="comment">//休眠1秒</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">    <span class="function">thread <span class="title">t3</span><span class="params">(f,<span class="number">3</span>,<span class="string">&quot;妲己&quot;</span>)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//3.仿函数</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">mythread1</span>&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">(<span class="type">int</span> bh,<span class="type">const</span> string&amp;str)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;第&quot;</span>&lt;&lt;i&lt;&lt;<span class="string">&quot;次表白&quot;</span>&lt;&lt;num&lt;&lt;<span class="string">&quot;号&quot;</span>&lt;&lt;str&lt;&lt;endl;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">1000</span>);<span class="comment">//休眠1秒</span></span><br><span class="line">       &#125;</span><br><span class="line">    &#125;   </span><br><span class="line">&#125;;</span><br><span class="line">    <span class="function">thread <span class="title">t4</span><span class="params">(mythread1(),<span class="number">3</span>,<span class="string">&quot;妲己&quot;</span>)</span></span>;</span><br><span class="line">    <span class="comment">//4.用类的静态成员函数创建线程</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">mythread2</span>&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> bh,<span class="type">const</span> string&amp;str)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;第&quot;</span>&lt;&lt;i&lt;&lt;<span class="string">&quot;次表白&quot;</span>&lt;&lt;num&lt;&lt;<span class="string">&quot;号&quot;</span>&lt;&lt;str&lt;&lt;endl;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">1000</span>);<span class="comment">//休眠1秒</span></span><br><span class="line">       &#125;</span><br><span class="line">    &#125;   </span><br><span class="line">&#125;;    </span><br><span class="line">    <span class="function">thread <span class="title">t5</span><span class="params">(mythread2::func,<span class="number">3</span>,<span class="string">&quot;妲己&quot;</span>)</span></span>;</span><br><span class="line">    <span class="comment">//5.用类的普通成员创建线程</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">mythread3</span>&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> bh,<span class="type">const</span> string&amp;str)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;第&quot;</span>&lt;&lt;i&lt;&lt;<span class="string">&quot;次表白&quot;</span>&lt;&lt;num&lt;&lt;<span class="string">&quot;号&quot;</span>&lt;&lt;str&lt;&lt;endl;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">1000</span>);<span class="comment">//休眠1秒</span></span><br><span class="line">       &#125;</span><br><span class="line">    &#125;   </span><br><span class="line">&#125;; </span><br><span class="line">    mythread3 myth;<span class="comment">//必须先创建类的对象，必须保证对象的生命周期比子线程要长</span></span><br><span class="line">    <span class="function">thread <span class="title">t6</span><span class="params">(&amp;mythread3::func,&amp;myth,<span class="number">3</span>,<span class="string">&quot;妲己&quot;</span>)</span></span>;<span class="comment">//第二个参数必需要填对象的this指针   </span></span><br><span class="line">    </span><br><span class="line">    cout&lt;&lt;<span class="string">&quot;任务开始\n&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;执行任务中。。。\n&quot;</span>;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    cout&lt;&lt;<span class="string">&quot;任务完成 \n&quot;</span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();<span class="comment">//回收资源</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="18-2-2-公共成员函数"><a href="#18-2-2-公共成员函数" class="headerlink" title="18.2.2 公共成员函数"></a>18.2.2 公共成员函数</h4><h5 id="2-1-get-id"><a href="#2-1-get-id" class="headerlink" title="2.1 get_id()"></a>2.1 get_id()</h5><p>应用程序启动之后默认只有一个线程，这个线程一般称之为主线程或父线程，通过线程类创建出的线程一般称之为子线程，每个被创建出的线程实例都对应一个线程 ID，这个 ID 是唯一的，可以通过这个 ID 来区分和识别各个已经存在的线程实例，这个获取线程 ID 的函数叫做 get_id()，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::<span class="function">thread::id <span class="title">get_id</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例程序如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> num, string str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;子线程: i = &quot;</span> &lt;&lt; i &lt;&lt; <span class="string">&quot;num: &quot;</span> </span><br><span class="line">             &lt;&lt; num &lt;&lt; <span class="string">&quot;, str: &quot;</span> &lt;&lt; str &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func1</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;子线程: i = &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;主线程的线程ID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func, <span class="number">520</span>, <span class="string">&quot;i love you&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func1)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;线程t 的线程ID: &quot;</span> &lt;&lt; t.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;线程t1的线程ID: &quot;</span> &lt;&lt; t1.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">//缺少对线程资源的回收t.join(),t1.join()</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">thread t1(func1)：子线程对象 t1 中的任务函数 func1()，没有参数，因此在线程构造函数中就无需指定了</span></span><br><span class="line"><span class="comment">通过线程对象调用 get_id() 就可以知道这个子线程的线程 ID 了，t.get_id()，t1.get_id()。</span></span><br></pre></td></tr></table></figure>

<p><strong>在上面的示例程序中有一个 bug，在主线程中依次创建出两个子线程，打印两个子线程的线程 ID，最后主线程执行完毕就退出了（主线程就是执行 main () 函数的那个线程）。默认情况下，主线程销毁时会将与其关联的两个子线程也一并销毁，但是这时有可能子线程中的任务还没有执行完毕，最后也就得不到我们想要的结果了。</strong></p>
<p>当启动了一个线程（创建了一个 thread 对象）之后，在这个线程结束的时候（std::terminate ()），我们如何去回收线程所使用的资源呢？thread 库给我们两种选择：</p>
<ul>
<li>加入式（join()）</li>
<li>分离式（detach()）</li>
</ul>
<p>另外，我们必须要在线程对象销毁之前在二者之间作出选择，否则程序运行期间就会有 bug 产生。</p>
<h5 id="2-2-join"><a href="#2-2-join" class="headerlink" title="2.2 join()"></a>2.2 join()</h5><p>join() 字面意思是连接一个线程，意味着主动地等待线程的终止（线程阻塞）。在某个线程中通过子线程对象调用 join() 函数，调用这个函数的线程被阻塞，但是子线程对象中的任务函数会继续执行，当任务执行完毕之后 join() 会清理当前子线程中的相关资源然后返回，同时，调用该函数的线程解除阻塞继续向下执行。</p>
<p>再次强调，我们一定要搞清楚这个函数阻塞的是哪一个线程，函数在哪个线程中被执行，那么函数就阻塞哪个线程。该函数的函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">join</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>

<p>有了这样一个线程阻塞函数之后，就可以解决在上面测试程序中的 bug 了，如果要阻塞主线程的执行，只需要在主线程中通过子线程对象调用这个方法即可，当调用这个方法的子线程对象中的任务函数执行完毕之后，主线程的阻塞也就随之解除了。修改之后的示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;主线程的线程ID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func, <span class="number">520</span>, <span class="string">&quot;i love you&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func1)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;线程t 的线程ID: &quot;</span> &lt;&lt; t.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;线程t1的线程ID: &quot;</span> &lt;&lt; t1.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">当主线程运行到第八行 t.join();，根据子线程对象 t 的任务函数 func() 的执行情况，主线程会做如下处理：</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">如果任务函数 func() 还没执行完毕，主线程阻塞，直到任务执行完毕，主线程解除阻塞，继续向下运行</span></span><br><span class="line"><span class="comment">如果任务函数 func() 已经执行完毕，主线程不会阻塞，继续向下运行</span></span><br></pre></td></tr></table></figure>

<h5 id="2-3-detach"><a href="#2-3-detach" class="headerlink" title="2.3 detach()"></a>2.3 detach()</h5><p>detach() 函数的作用是进行线程分离，分离主线程和创建出的子线程。在线程分离之后，主线程退出也会一并销毁创建出的所有子线程，在主线程退出之前，它可以脱离主线程继续独立的运行，任务执行完毕之后，这个子线程会自动释放自己占用的系统资源。（其实就是孩子翅膀硬了，和家里断绝关系，自己外出闯荡了，如果家里被诛九族还是会受牵连）。该函数函数原型如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void detach();</span><br></pre></td></tr></table></figure>

<p>线程分离函数没有参数也没有返回值，只需要在线程成功之后，通过线程对象调用该函数即可，继续将上面的测试程序修改一下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;主线程的线程ID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func, <span class="number">520</span>, <span class="string">&quot;i love you&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func1)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;线程t 的线程ID: &quot;</span> &lt;&lt; t.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;线程t1的线程ID: &quot;</span> &lt;&lt; t1.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line">    t1.<span class="built_in">detach</span>();</span><br><span class="line">    <span class="comment">// 让主线程休眠, 等待子线程执行完毕</span></span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">5</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>注意事项：</strong> 线程分离函数 detach () 不会阻塞线程，子线程和主线程分离之后，在主线程中就不能再对这个子线程做任何控制了，比如：通过 join () 阻塞主线程等待子线程中的任务执行完毕，或者调用 get_id () 获取子线程的线程 ID。有利就有弊，鱼和熊掌不可兼得，建议使用 join ()。</p>
<h5 id="2-4-joinable"><a href="#2-4-joinable" class="headerlink" title="2.4 joinable()"></a>2.4 joinable()</h5><p>joinable() 函数用于判断主线程和子线程是否处理关联（连接）状态，一般情况下，二者之间的关系处于关联状态，该函数返回一个布尔类型：</p>
<ul>
<li>返回值为 true：主线程和子线程之间有关联（连接）关系</li>
<li>返回值为 false：主线程和子线程之间没有关联（连接）关系</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">joinable</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line">示例：</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    thread t;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;before starting, joinable: &quot;</span> &lt;&lt; t.<span class="built_in">joinable</span>() &lt;&lt; endl;<span class="comment">//0</span></span><br><span class="line"></span><br><span class="line">    t = <span class="built_in">thread</span>(foo);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after starting, joinable: &quot;</span> &lt;&lt; t.<span class="built_in">joinable</span>() &lt;&lt; endl;<span class="comment">//1</span></span><br><span class="line"></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after joining, joinable: &quot;</span> &lt;&lt; t.<span class="built_in">joinable</span>() &lt;&lt; endl;<span class="comment">//0</span></span><br><span class="line"></span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(foo)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after starting, joinable: &quot;</span> &lt;&lt; t1.<span class="built_in">joinable</span>() &lt;&lt; endl;<span class="comment">//1</span></span><br><span class="line">    t1.<span class="built_in">detach</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after detaching, joinable: &quot;</span> &lt;&lt; t1.<span class="built_in">joinable</span>() &lt;&lt; endl;<span class="comment">//0</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="2-5-operator-x3D"><a href="#2-5-operator-x3D" class="headerlink" title="2.5 operator&#x3D;"></a>2.5 operator&#x3D;</h5><p>线程中的资源是不能被复制的，因此通过 &#x3D; 操作符进行赋值操作<strong>最终并不会得到</strong>两个完全相同的对象。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// move (1)	</span></span><br><span class="line">thread&amp; <span class="keyword">operator</span>= (thread&amp;&amp; other) <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// copy [deleted] (2)	</span></span><br><span class="line">thread&amp; <span class="keyword">operator</span>= (<span class="type">const</span> other&amp;) = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>

<p>通过以上 &#x3D; 操作符的重载声明可以得知：</p>
<ul>
<li>如果 other 是一个右值，会进行资源所有权的转移</li>
<li>如果 other 不是右值，禁止拷贝，该函数被显示删除（&#x3D;delete），不可用</li>
</ul>
<h4 id="18-2-3-静态函数"><a href="#18-2-3-静态函数" class="headerlink" title="18.2.3 静态函数"></a>18.2.3 静态函数</h4><p>thread 线程类还提供了一个静态方法，用于获取当前计算机的 CPU 核心数，根据这个结果在程序中创建出数量相等的线程，每个线程独自占有一个CPU核心，这些线程就不用分时复用CPU时间片，此时程序的并发效率是最高的。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="type">unsigned</span> <span class="title">hardware_concurrency</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> num = thread::<span class="built_in">hardware_concurrency</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;CPU number: &quot;</span> &lt;&lt; num &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="18-3-线程命名空间-this-thread"><a href="#18-3-线程命名空间-this-thread" class="headerlink" title="18.3 线程命名空间 this_thread"></a>18.3 线程命名空间 this_thread</h3><p>在 C++11 中不仅添加了线程类，还添加了一个关于线程的命名空间 std::this_thread，在这个命名空间中提供了四个公共的成员函数，通过这些成员函数就可以对当前线程进行相关的操作了。</p>
<h4 id="18-3-1-get-id"><a href="#18-3-1-get-id" class="headerlink" title="18.3.1 get_id()"></a>18.3.1 get_id()</h4><p>调用命名空间 std::this_thread 中的 get_id() 方法可以得到当前线程的线程 ID，函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">thread::id <span class="title">get_id</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line">示例：</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;子线程: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;主线程: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="18-3-2-sleep-for"><a href="#18-3-2-sleep-for" class="headerlink" title="18.3.2 sleep_for()"></a>18.3.2 sleep_for()</h4><p>进程被创建后有五种状态，同样地线程被创建后也有这五种状态：创建态，就绪态，运行态，阻塞态(挂起态)，退出态(终止态) ，关于状态之间的转换是一样的，请参考进程，线程和进程的执行有很多相似之处，在计算机中启动的多个线程都需要占用 CPU 资源，但是 CPU 的个数是有限的并且每个 CPU 在同一时间点不能同时处理多个任务。为了能够实现并发处理，多个线程都是分时复用CPU时间片，快速的交替处理各个线程中的任务。因此多个线程之间需要争抢CPU时间片，抢到了就执行，抢不到则无法执行（因为默认所有的线程优先级都相同，内核也会从中调度，不会出现某个线程永远抢不到 CPU 时间片的情况）。</p>
<p>命名空间 this_thread 中提供了一个休眠函数 sleep_for()，调用这个函数的线程会马上从运行态变成阻塞态并在这种状态下休眠一定的时长，因为阻塞态的线程已经让出了 CPU 资源，代码也不会被执行，所以线程休眠过程中对 CPU 来说没有任何负担。这个函数是函数原型如下，参数需要指定一个休眠时长，是一个时间段：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sleep_for</span> <span class="params">(<span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</span></span>;\</span><br><span class="line">示例：</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;子线程: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, i = &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">在 func() 函数的 for 循环中使用了 this_thread::sleep_for(chrono::seconds(1)); 之后，每循环一次程序都会阻塞 1 秒钟，也就是说每隔 1 秒才会进行一次输出。需要注意的是：程序休眠完成之后，会从阻塞态重新变成就绪态，就绪态的线程需要再次争抢 CPU 时间片，抢到之后才会变成运行态，这时候程序才会继续向下运行。</span></span><br></pre></td></tr></table></figure>

<h4 id="18-3-3-sleep-until"><a href="#18-3-3-sleep-until" class="headerlink" title="18.3.3 sleep_until()"></a>18.3.3 sleep_until()</h4><p>命名空间 this_thread 中提供了另一个休眠函数 sleep_until()，和 sleep_for() 不同的是它的参数类型不一样</p>
<ul>
<li>sleep_until()：指定线程阻塞到某一个指定的时间点 time_point类型，之后解除阻塞</li>
<li>sleep_for()：指定线程阻塞一定的时间长度 duration 类型，之后解除阻塞</li>
</ul>
<p>该函数的函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>&gt;</span><br><span class="line">  <span class="function"><span class="type">void</span> <span class="title">sleep_until</span> <span class="params">(<span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time)</span></span>;</span><br><span class="line">示例程序如下：</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 获取当前系统时间点</span></span><br><span class="line">        <span class="keyword">auto</span> now = chrono::system_clock::<span class="built_in">now</span>();</span><br><span class="line">        <span class="comment">// 时间间隔为2s</span></span><br><span class="line">        <span class="function">chrono::seconds <span class="title">sec</span><span class="params">(<span class="number">2</span>)</span></span>;</span><br><span class="line">        <span class="comment">// 当前时间点之后休眠两秒</span></span><br><span class="line">        this_thread::<span class="built_in">sleep_until</span>(now + sec);</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;子线程: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, i = &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>sleep_until() 和 sleep_for() 函数的功能是一样的，只不过前者是基于时间点去阻塞线程，后者是基于时间段去阻塞线程，项目开发过程中根据实际情况选择最优的解决方案即可。</p>
<h4 id="18-3-4-yield"><a href="#18-3-4-yield" class="headerlink" title="18.3.4 yield()"></a>18.3.4 yield()</h4><p>命名空间 this_thread 中提供了一个非常绅士的函数 yield()，在线程中调用这个函数之后，处于运行态的线程会主动让出自己已经抢到的 CPU 时间片，最终变为就绪态，这样其它的线程就有更大的概率能够抢到 CPU 时间片了。使用这个函数的时候需要注意一点，线程调用了 yield () 之后会主动放弃 CPU 资源，但是这个变为就绪态的线程会马上参与到下一轮 CPU 的抢夺战中，不排除它能继续抢到 CPU 时间片的情况，这是概率问题。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">yield</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line">示例：</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100000000000</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;子线程: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, i = &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">        this_thread::<span class="built_in">yield</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结论：std::this_thread::yield() 的目的是避免一个线程长时间占用CPU资源，从而导致多线程处理性能下降；它是让当前线程主动放弃了当前自己抢到的CPU资源，但是在下一轮还会继续抢。</strong></p>
<h3 id="18-4-call-once-函数"><a href="#18-4-call-once-函数" class="headerlink" title="18.4 call_once 函数"></a>18.4 call_once 函数</h3><p>在某些特定情况下，某些函数只能在多线程环境下调用一次，比如：要初始化某个对象，而这个对象只能被初始化一次，就可以使用 std::call_once() 来保证函数在多线程环境下只能被调用一次。使用 call_once() 的时候，需要一个 once_flag 作为 call_once() 的传入参数，该函数的原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;mutex&gt;</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt; <span class="keyword">class</span> Callable, <span class="keyword">class</span>... Args &gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">call_once</span><span class="params">( std::once_flag&amp; flag, Callable&amp;&amp; f, Args&amp;&amp;... args )</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">flag：once_flag 类型的对象，要保证这个对象能够被多个线程同时访问到</span></span><br><span class="line"><span class="comment">f：回调函数，可以传递一个有名函数地址，也可以指定一个匿名函数</span></span><br><span class="line"><span class="comment">args：作为实参传递给回调函数</span></span><br></pre></td></tr></table></figure>

<p>多线程操作过程中，std::call_once() 内部的回调函数只会被执行一次，示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line">once_flag g_flag;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">do_once</span><span class="params">(<span class="type">int</span> a, string b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;name: &quot;</span> &lt;&lt; b &lt;&lt; <span class="string">&quot;, age: &quot;</span> &lt;&lt; a &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">do_something</span><span class="params">(<span class="type">int</span> age, string name)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> num = <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">call_once</span>(g_flag, do_once, <span class="number">19</span>, <span class="string">&quot;luffy&quot;</span>);       <span class="comment">//do_once回调函数只会调用一次</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;do_something() function num = &quot;</span> &lt;&lt; num++ &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(do_something, <span class="number">20</span>, <span class="string">&quot;ace&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(do_something, <span class="number">20</span>, <span class="string">&quot;sabo&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t3</span><span class="params">(do_something, <span class="number">19</span>, <span class="string">&quot;luffy&quot;</span>)</span></span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*输出</span></span><br><span class="line"><span class="comment">name: luffy, age: 19</span></span><br><span class="line"><span class="comment">do_something() function num = 1</span></span><br><span class="line"><span class="comment">do_something() function num = 2</span></span><br><span class="line"><span class="comment">do_something() function num = 3</span></span><br></pre></td></tr></table></figure>

<h3 id="18-5-native-handle函数"><a href="#18-5-native-handle函数" class="headerlink" title="18.5 native_handle函数"></a>18.5 native_handle函数</h3><p>C++11定义了线程标准，不同平台和编译器在实现的时候，本质上都是对操作系统的线程库进行封装，会损失一部分功能。为了弥补C++11线程库的不足，thread类提供了native_handle()成员函数，用于获得与操作系统相关的<strong>原生线程句柄</strong>，操作系统原生的线程库就可以<strong>用原生线程句柄操作线程</strong>。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)&#123;</span><br><span class="line">        cout&lt;&lt;i&lt;&lt;endl;</span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));<span class="comment">//休眠1秒·</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">tt</span><span class="params">(func)</span></span>;   <span class="comment">//创建线程</span></span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">5</span>)); <span class="comment">//休眠5秒</span></span><br><span class="line">    <span class="type">pthread_t</span> thid = tt.<span class="built_in">native_handle</span>();<span class="comment">//获取Linux操作系统原生的线程句柄</span></span><br><span class="line">    <span class="built_in">pthread_cancel</span>(thid);   <span class="comment">//取消线程</span></span><br><span class="line">    tt.<span class="built_in">join</span>(); <span class="comment">//等待线程退出并回收资源</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="18-6-线程同步之互斥锁-mutex"><a href="#18-6-线程同步之互斥锁-mutex" class="headerlink" title="18.6 线程同步之互斥锁 mutex"></a>18.6 线程同步之互斥锁 mutex</h3><p>解决多线程数据混乱的方案就是进行线程同步，最常用的就是互斥锁，在 C++11 中一共提供了四种互斥锁：</p>
<ul>
<li>std::mutex：独占的互斥锁，不能递归使用</li>
<li>std::timed_mutex：带超时的独占互斥锁，不能递归使用</li>
<li>std::recursive_mutex：递归互斥锁，不带超时功能</li>
<li>std::recursive_timed_mutex：带超时的递归互斥锁</li>
</ul>
<p>互斥锁在有些资料中也被称之为互斥量，二者是一个东西。</p>
<h4 id="18-6-1-std-mutex"><a href="#18-6-1-std-mutex" class="headerlink" title="18.6.1 std::mutex"></a>18.6.1 std::mutex</h4><h5 id="1-1-成员函数"><a href="#1-1-成员函数" class="headerlink" title="1.1 成员函数"></a>1.1 成员函数</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">lock</span>() 函数用于给临界区加锁，并且只能有一个线程获得锁的所有权，它有阻塞线程的作用，函数原型如下：</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">()</span></span>;</span><br><span class="line">除了使用 <span class="built_in">lock</span>() 还可以使用 <span class="built_in">try_lock</span>() 获取互斥锁的所有权并对互斥锁加锁，函数原型如下:</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>

<p>二者的区别在于 try_lock() 不会阻塞线程，lock() 会阻塞线程：</p>
<ul>
<li>如果互斥锁是未锁定状态，得到了互斥锁所有权并加锁成功，函数返回 true</li>
<li>如果互斥锁是锁定状态，无法得到互斥锁所有权加锁失败，函数返回 false</li>
</ul>
<p>当互斥锁被锁定之后可以通过 unlock() 进行解锁，但是需要注意的是只有拥有互斥锁所有权的线程也就是对互斥锁上锁的线程才能将其解锁，其它线程是没有权限做这件事情的。该函数的函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>

<p><strong>线程同步的目的是让多线程按照顺序依次执行临界区代码，这样做线程对共享资源的访问就从并行访问变为了线性访问，访问效率降低了，但是保证了数据的正确性。</strong></p>
<p>注：当线程对互斥锁对象加锁，并且执行完临界区代码之后，一定要使用这个线程对互斥锁解锁，否则最终会造成线程的死锁。死锁之后当前应用程序中的所有线程都会被阻塞，并且阻塞无法解除，应用程序也无法继续运行。</p>
<h5 id="1-2-线程同步"><a href="#1-2-线程同步" class="headerlink" title="1.2 线程同步"></a>1.2 线程同步</h5><p>举个栗子，我们让两个线程共同操作同一个全局变量，二者交替数数，将数值存储到这个全局变量里边并打印出来。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> g_num = <span class="number">0</span>;  <span class="comment">// 为 g_num_mutex 所保护</span></span><br><span class="line">mutex g_num_mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">slow_increment</span><span class="params">(<span class="type">int</span> id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; ++i) </span><br><span class="line">    &#123;</span><br><span class="line">        g_num_mutex.<span class="built_in">lock</span>(); <span class="comment">//加锁</span></span><br><span class="line">        ++g_num;</span><br><span class="line">        cout &lt;&lt; id &lt;&lt; <span class="string">&quot; =&gt; &quot;</span> &lt;&lt; g_num &lt;&lt; endl;</span><br><span class="line">        g_num_mutex.<span class="built_in">unlock</span>();<span class="comment">//解锁</span></span><br><span class="line"></span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(slow_increment, <span class="number">0</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(slow_increment, <span class="number">1</span>)</span></span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的示例程序中，两个子线程执行的任务的一样的（其实也可以不一样，不同的任务中也可以对共享资源进行读写操作），在任务函数中把与全局变量相关的代码加了锁，两个线程只能顺序访问这部分代码（如果不进行线程同步打印出的数据是混乱且无序的）。另外需要强调一点：</p>
<ol>
<li><strong>在所有线程的任务函数执行完毕之前，互斥锁对象是不能被析构的，一定要在程序中保证这个对象的可用性。</strong></li>
<li><strong>互斥锁的个数和共享资源的个数相等，也就是说每一个共享资源都应该对应一个互斥锁对象。互斥锁对象的个数和线程的个数没有关系。</strong></li>
</ol>
<h4 id="18-6-2-std-look-guard"><a href="#18-6-2-std-look-guard" class="headerlink" title="18.6.2 std::look_guard"></a>18.6.2 std::look_guard</h4><p>lock_guard 是 C++11 新增的一个模板类，使用这个类，可以简化互斥锁 lock() 和 unlock() 的写法，同时也更安全。这个模板类的定义和常用的构造函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 类的定义，定义于头文件 &lt;mutex&gt;</span></span><br><span class="line"><span class="keyword">template</span>&lt; <span class="keyword">class</span> <span class="title class_">Mutex</span> &gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">lock_guard</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 常用构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">lock_guard</span><span class="params">( mutex_type&amp; m )</span></span>;</span><br></pre></td></tr></table></figure>

<p>lock_guard 在使用上面提供的这个构造函数构造对象时，会自动锁定互斥量，而在退出作用域后进行析构时就会自动解锁，从而保证了互斥量的正确操作，避免忘记 unlock() 操作而导致线程死锁。lock_guard 使用了 RAII 技术，就是在类构造函数中分配资源，在析构函数中释放资源，保证资源出了作用域就释放。</p>
<p>使用 lock_guard 对上面的例子进行修改，代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">slow_increment</span><span class="params">(<span class="type">int</span> id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; ++i) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 使用哨兵锁管理互斥锁</span></span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">lock</span><span class="params">(g_num_mutex)</span></span>;</span><br><span class="line">        ++g_num;</span><br><span class="line">        cout &lt;&lt; id &lt;&lt; <span class="string">&quot; =&gt; &quot;</span> &lt;&lt; g_num &lt;&lt; endl;</span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过修改发现代码被精简了，而且不用担心因为忘记解锁而造成程序的死锁，<strong>但是这种方式也有弊端，在上面的示例程序中整个for循环的体都被当做了临界区，多个线程是线性的执行临界区代码的，因此临界区越大程序效率越低，</strong>还是需要根据实际情况选择最优的解决方案。</p>
<h4 id="18-6-3-std-recursive-mutex"><a href="#18-6-3-std-recursive-mutex" class="headerlink" title="18.6.3 std::recursive_mutex"></a>18.6.3 std::recursive_mutex</h4><p>递归互斥锁 std::recursive_mutex 允许同一线程多次获得互斥锁，可以用来解决同一线程需要多次获取互斥量时死锁的问题，在下面的例子中使用独占非递归互斥量会发生死锁：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Calculate</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">Calculate</span>() : <span class="built_in">m_i</span>(<span class="number">6</span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">mul</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;<span class="comment">//lock_guard&lt;recursive_mutex&gt; locker(m_mutex);</span></span><br><span class="line">        m_i *= x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">div</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;<span class="comment">//lock_guard&lt;recursive_mutex&gt; locker(m_mutex);</span></span><br><span class="line">        m_i /= x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">both</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;<span class="comment">//lock_guard&lt;recursive_mutex&gt; locker(m_mutex);</span></span><br><span class="line">        <span class="built_in">mul</span>(x);</span><br><span class="line">        <span class="built_in">div</span>(y);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_i;</span><br><span class="line">    mutex m_mutex; <span class="comment">// recursive_mutex m_mutex;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Calculate cal;</span><br><span class="line">    cal.<span class="built_in">both</span>(<span class="number">6</span>, <span class="number">3</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;<span class="comment">/*</span></span><br><span class="line"><span class="comment">上面的程序中执行了 cal.both(6, 3); 调用之后，程序就会发生死锁，在 both() 中已经对互斥锁加锁了，继续调用 mult() 函数，已经得到互斥锁所有权的线程再次获取这个互斥锁的所有权就会造成死锁,要解决这个死锁的问题，一个简单的办法就是使用递归互斥锁 std::recursive_mutex，它允许一个线程多次获得互斥锁的所有权。修改之后的代码如上注释内容：</span></span><br></pre></td></tr></table></figure>

<p>虽然递归互斥锁可以解决同一个互斥锁频繁获取互斥锁资源的问题，但是还是建议少用，主要原因如下：</p>
<ul>
<li>使用递归互斥锁的场景往往都是可以简化的，使用递归互斥锁很容易放纵复杂逻辑的产生，从而导致bug的产生</li>
<li>递归互斥锁比非递归互斥锁<strong>效率要低一些。</strong></li>
<li>递归互斥锁虽然允许同一个线程多次获得同一个互斥锁的所有权，但最大次数并未具体说明，一旦超过一定的次数，就会抛出std::system错误。</li>
</ul>
<h4 id="18-6-4-std-time-mutex"><a href="#18-6-4-std-time-mutex" class="headerlink" title="18.6.4 std::time_mutex"></a>18.6.4 std::time_mutex</h4><p>std::timed_mutex 是超时独占互斥锁，主要是在获取互斥锁资源时增加了超时等待功能，因为不知道获取锁资源需要等待多长时间，为了保证不一直等待下去，设置了一个超时时长，超时后线程就可以解除阻塞去做其他事情了。</p>
<p>std::timed_mutex 比 std::_mutex 多了两个成员函数：try_lock_for() 和 try_lock_until(),函数原型如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// std::timed_mutex比std::_mutex多出的两个成员函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>&gt;</span><br><span class="line">  <span class="function"><span class="type">bool</span> <span class="title">try_lock_for</span> <span class="params">(<span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock_until</span> <span class="params">(<span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">try_lock_for 函数是当线程获取不到互斥锁资源的时候，让线程阻塞一定的时间长度</span></span><br><span class="line"><span class="comment">try_lock_until 函数是当线程获取不到互斥锁资源的时候，让线程阻塞到某一个指定的时间点</span></span><br></pre></td></tr></table></figure>

<p>下面的示例程序中为大家演示了 std::timed_mutex 的使用：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line">timed_mutex g_mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">work</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">chrono::seconds <span class="title">timeout</span><span class="params">(<span class="number">1</span>)</span></span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 通过阻塞一定的时长来争取得到互斥锁所有权</span></span><br><span class="line">        <span class="keyword">if</span> (g_mutex.<span class="built_in">try_lock_for</span>(timeout))</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;当前线程ID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() </span><br><span class="line">                &lt;&lt; <span class="string">&quot;, 得到互斥锁所有权...&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="comment">// 模拟处理任务用了一定的时长</span></span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">10</span>));</span><br><span class="line">            <span class="comment">// 互斥锁解锁</span></span><br><span class="line">            g_mutex.<span class="built_in">unlock</span>();</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;当前线程ID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() </span><br><span class="line">                &lt;&lt; <span class="string">&quot;, 没有得到互斥锁所有权...&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="comment">// 模拟处理其他任务用了一定的时长</span></span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(<span class="number">50</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(work)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(work)</span></span>;</span><br><span class="line"></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="18-7-线程同步之条件变量"><a href="#18-7-线程同步之条件变量" class="headerlink" title="18.7* 线程同步之条件变量"></a>18.7* 线程同步之条件变量</h3><p>条件变量是 C++11 提供的另外一种用于等待的同步机制，它能阻塞一个或多个线程，直到收到另外一个线程发出的通知或者超时时，才会唤醒当前阻塞的线程。条件变量需要和互斥量配合起来使用，C++11 提供了两种条件变量：</p>
<ul>
<li><p>condition_variable：需要配合 std::unique_lock<a href="std::mutex">std::mutex</a> 进行 wait 操作，也就是阻塞线程的操作。</p>
</li>
<li><p>condition_variable_any：可以和任意带有 lock()、unlock() 语义的 mutex 搭配（包括用户自定义的锁类型）使用，也就是说有四种：</p>
<p><strong>（也就是说这四种锁需要使用condition_variable_any，condition_variable只适用一种锁即mutex！！！！！）</strong></p>
<ul>
<li>std::mutex：独占的非递归互斥锁</li>
<li>std::timed_mutex：带超时的独占非递归互斥锁</li>
<li>std::recursive_mutex：不带超时功能的递归互斥锁</li>
<li>std::recursive_timed_mutex：带超时的递归互斥锁</li>
</ul>
</li>
</ul>
<p>条件变量通常用于生产者和消费者模型，大致使用过程如下：</p>
<ol>
<li><p>拥有条件变量的线程获取互斥量</p>
</li>
<li><p>循环检查某个条件，如果条件不满足阻塞当前线程，否则线程继续向下执行</p>
<ul>
<li>产品的数量达到上限，生产者阻塞，否则生产者一直生产。。。</li>
<li>产品的数量为零，消费者阻塞，否则消费者一直消费。。。</li>
</ul>
</li>
<li><p>条件满足之后，可以调用 notify_one() 或者 notify_all() 唤醒一个或者所有被阻塞的线程</p>
<ul>
<li>由消费者唤醒被阻塞的生产者，生产者解除阻塞继续生产。。。</li>
<li>由生产者唤醒被阻塞的消费者，消费者解除阻塞继续消费。。。</li>
</ul>
</li>
</ol>
<h4 id="18-7-1-成员函数"><a href="#18-7-1-成员函数" class="headerlink" title="18.7.1 成员函数"></a>18.7.1 成员函数</h4><p>condition_variable 的成员函数主要分为两部分：<strong>线程等待（阻塞）函数</strong> 和<strong>线程通知（唤醒）函数</strong>，这些函数被定义于头文件 <condition_variable>。</p>
<ol>
<li><p>等待函数：调用wait()函数的线程会被阻塞。<strong>wait()运行流程：1.把互斥锁解开 2.阻塞，等待被唤醒 3. 给互斥锁加锁</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ①</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">wait</span> <span class="params">(unique_lock&lt;mutex&gt;&amp; lck)</span></span>;<span class="comment">//调用该函数的线程直接被阻塞</span></span><br><span class="line"><span class="comment">// ②</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Predicate</span>&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">wait</span> <span class="params">(unique_lock&lt;mutex&gt;&amp; lck, Predicate pred)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">函数②：该函数的第二个参数是一个判断条件，是一个返回值为布尔类型的函数</span></span><br><span class="line"><span class="comment">1.该参数可以传递一个有名函数的地址，也可以直接指定一个匿名函数</span></span><br><span class="line"><span class="comment">2.表达式返回false当前线程被阻塞，表达式返回true当前线程不会被阻塞，继续向下执行</span></span><br></pre></td></tr></table></figure>

<ul>
<li>独占的互斥锁对象不能直接传递给 wait() 函数，需要通过模板类 unique_lock 进行二次处理，通过得到的对象仍然可以对独占的互斥锁对象做如下操作，使用起来更灵活。</li>
<li>如果线程被该函数阻塞，这个线程会释放占有的互斥锁的所有权，当阻塞解除之后这个线程会重新得到互斥锁的所有权，继续向下执行（这个过程是在函数内部完成的，了解这个过程即可，其目的是为了避免线程的死锁）。</li>
</ul>
</li>
</ol>
<p>wait_for() 函数和 wait() 的功能是一样的，只不过多了一个阻塞时长，假设阻塞的线程没有被其他线程唤醒，当阻塞时长用完之后，线程就会自动解除阻塞，继续向下执行。</p>
<p>wait_until() 函数和 wait_for() 的功能是一样的，它是指定让线程阻塞到某一个时间点，假设阻塞的线程没有被其他线程唤醒，当到达指定的时间点之后，线程就会自动解除阻塞，继续向下执行。</p>
<ol start="2">
<li><p>通知函数</p>
  <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">notify_one</span><span class="params">()</span> <span class="keyword">noexcept</span></span>; <span class="comment">//唤醒一个被当前条件变量阻塞的线程</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">notify_all</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;  <span class="comment">//唤醒全部被当前条件变量阻塞的线程</span></span><br></pre></td></tr></table></figure></li>
</ol>
<h4 id="18-7-2-生产者-消费者模型示例："><a href="#18-7-2-生产者-消费者模型示例：" class="headerlink" title="18.7.2 生产者-消费者模型示例："></a>18.7.2 生产者-消费者模型示例：</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;thread&gt;</span> <span class="comment">//线程类头文件</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;mutex&gt;</span>   <span class="comment">//互斥锁类的头文件</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;deque&gt;</span>   </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;condition_variable&gt;</span>  <span class="comment">//条件变量的头文件</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AA</span> &#123;</span><br><span class="line">	mutex m_mutex;<span class="comment">//互斥锁</span></span><br><span class="line">	condition_variable m_cond; <span class="comment">//条件变量</span></span><br><span class="line">	queue&lt;string, deque&lt;string&gt;&gt; m_q; <span class="comment">//缓存队列，底层容器用deque</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="comment">//生产者任务函数</span></span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">incache</span><span class="params">(<span class="type">int</span> num)</span> <span class="comment">//生产数据，num指定数据的个数</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="function">lock_guard&lt;mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;<span class="comment">//申请加锁</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; num;i++) &#123;</span><br><span class="line">			<span class="type">static</span> <span class="type">int</span> bh = <span class="number">1</span>;<span class="comment">//超女编号</span></span><br><span class="line">			string message = <span class="built_in">to_string</span>(bh++) + <span class="string">&quot;号超女&quot;</span>;</span><br><span class="line">			m_q.<span class="built_in">push</span>(message);<span class="comment">//把生产出来的数据入队</span></span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//向消费者线程发送通知，告诉他们有数据需要处理</span></span><br><span class="line">		m_cond.<span class="built_in">notify_one</span>();<span class="comment">//唤醒一个被当前条件变量阻塞的线程</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//消费者线程任务函数</span></span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">outcache</span><span class="params">()</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="keyword">while</span>(<span class="literal">true</span>)</span><br><span class="line"></span><br><span class="line">		&#123;</span><br><span class="line">			string message;</span><br><span class="line">			&#123; <span class="comment">//加上作用域，释放锁</span></span><br><span class="line">				<span class="comment">//把互斥锁转换为unique_lock&lt;mutex&gt;并申请加锁</span></span><br><span class="line">				<span class="function">unique_lock&lt;mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">				<span class="keyword">while</span> (m_q.<span class="built_in">empty</span>()) <span class="comment">//如果缓存队列非空，不进入循环，直接处理数据，不能用if语句</span></span><br><span class="line">				&#123;  </span><br><span class="line">                    <span class="comment">//wait()运行流程：1.把互斥锁解开 2.阻塞，等待被唤醒 3. 给互斥锁加锁</span></span><br><span class="line">					m_cond.<span class="built_in">wait</span>(lock);<span class="comment">//等待生产者的唤醒信号,</span></span><br><span class="line">				&#125;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">//m_cond.wait(lock,[this]&#123;return !m_q.empty();&#125;);   //利用wait()的重载，与while循环一样的效果</span></span><br><span class="line">                </span><br><span class="line">				<span class="comment">//数据元素出队</span></span><br><span class="line">			    message = m_q.<span class="built_in">front</span>();</span><br><span class="line">				m_q.<span class="built_in">pop</span>();</span><br><span class="line">                cout &lt;&lt; <span class="string">&quot;线程：&quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; message &lt;&lt; endl;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="comment">//处理出队的数据（把数据消费掉）</span></span><br><span class="line">			this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));<span class="comment">//假设处理数据需要1毫秒</span></span><br><span class="line">				</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	AA aa;</span><br><span class="line">	<span class="function">thread <span class="title">t1</span><span class="params">(&amp;AA::outcache, &amp;aa)</span></span>;<span class="comment">//创建消费者线程t1</span></span><br><span class="line">	<span class="function">thread <span class="title">t2</span><span class="params">(&amp;AA::outcache, &amp;aa)</span></span>; <span class="comment">//t2</span></span><br><span class="line">	<span class="function">thread <span class="title">t3</span><span class="params">(&amp;AA::outcache, &amp;aa)</span></span>;  <span class="comment">//t3</span></span><br><span class="line"></span><br><span class="line">	this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">2</span>));<span class="comment">//休眠2秒</span></span><br><span class="line">	aa.<span class="built_in">incache</span>(<span class="number">3</span>); <span class="comment">//生产3个数据</span></span><br><span class="line"></span><br><span class="line">	this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">2</span>)); <span class="comment">//休眠3秒</span></span><br><span class="line">	aa.<span class="built_in">incache</span>(<span class="number">5</span>); <span class="comment">//生产5个数据</span></span><br><span class="line"></span><br><span class="line">	t1.<span class="built_in">join</span>();  <span class="comment">//回收子线程的资源</span></span><br><span class="line">	t2.<span class="built_in">join</span>();</span><br><span class="line">	t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="18-8-线程同步之原子变量-atomic"><a href="#18-8-线程同步之原子变量-atomic" class="headerlink" title="18.8* 线程同步之原子变量 atomic"></a>18.8* 线程同步之原子变量 atomic</h3><p>由于原子操作是通过指令提供的支持，因此它的性能相比锁和消息传递会好很多。相比较于锁而言，原子类型不需要开发者处理加锁和释放锁的问题，同时支持修改，读取等操作，还具备较高的并发性能，几乎所有的语言都支持原子类型。</p>
<p>可以看出原子类型是无锁类型，但是无锁不代表无需等待，因为原子类型内部使用了 CAS 循环，当大量的冲突发生时，该等待还是得等待！但是总归比锁要好。</p>
<p>C++11 内置了整形的原子变量，这样就可以更方便的使用原子变量了。在多线程操作中，使用原子变量之后就不需要再使用互斥量来保护该变量了，用起来更简洁。因为对原子变量进行的操作只能是一个原子操作（atomic operation），原子操作指的是不会被线程调度机制打断的操作，这种操作一旦开始，就一直运行到结束，中间不会有任何的上下文切换。多线程同时访问共享资源造成数据混乱的原因就是因为 CPU 的上下文切换导致的，使用原子变量解决了这个问题，因此互斥锁的使用也就不再需要了。</p>
<h4 id="18-8-1-atomic-类成员"><a href="#18-8-1-atomic-类成员" class="headerlink" title="18.8.1 atomic 类成员"></a>18.8.1 atomic 类成员</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;atomic&gt;</span></span><br><span class="line"><span class="keyword">template</span>&lt; <span class="keyword">class</span> <span class="title class_">T</span> &gt;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">atomic</span>;</span><br><span class="line">通过定义可得知：在使用这个模板类的时候，一定要指定模板类型。</span><br></pre></td></tr></table></figure>

<p><strong>构造函数：</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ①</span></span><br><span class="line"><span class="built_in">atomic</span>() <span class="keyword">noexcept</span> = <span class="keyword">default</span>;  <span class="comment">//默认无参构造函数。</span></span><br><span class="line"><span class="comment">// ②</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="title">atomic</span><span class="params">( T desired )</span> <span class="keyword">noexcept</span></span>; <span class="comment">//使用 desired 初始化原子变量的值。</span></span><br><span class="line"><span class="comment">// ③</span></span><br><span class="line"><span class="built_in">atomic</span>( <span class="type">const</span> atomic&amp; ) = <span class="keyword">delete</span>;  <span class="comment">//使用 =delete 显示删除拷贝构造函数，不允许进行对象之间的拷贝</span></span><br></pre></td></tr></table></figure>

<p><strong>公共成员函数</strong></p>
<p>原子类型在类内部重载了 &#x3D; 操作符，并且不允许在类的外部使用 &#x3D; 进行对象的拷贝。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">T <span class="keyword">operator</span>=( T desired ) <span class="keyword">noexcept</span>;</span><br><span class="line">T <span class="keyword">operator</span>=( T desired ) <span class="keyword">volatile</span> <span class="keyword">noexcept</span>;</span><br><span class="line"></span><br><span class="line">atomic&amp; <span class="keyword">operator</span>=( <span class="type">const</span> atomic&amp; ) = <span class="keyword">delete</span>;</span><br><span class="line">atomic&amp; <span class="keyword">operator</span>=( <span class="type">const</span> atomic&amp; ) <span class="keyword">volatile</span> = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>

<h4 id="18-8-2-原子变量的使用"><a href="#18-8-2-原子变量的使用" class="headerlink" title="18.8.2 原子变量的使用"></a>18.8.2 原子变量的使用</h4><p>假设我们要制作一个多线程交替数数的计数器，我们使用原子变量的方式进行实现:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Counter</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">increment</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            m_value++;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;increment number: &quot;</span> &lt;&lt; m_value</span><br><span class="line">                &lt;&lt; <span class="string">&quot;, theadID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(<span class="number">500</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">decrement</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            m_value--;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;decrement number: &quot;</span> &lt;&lt; m_value</span><br><span class="line">                &lt;&lt; <span class="string">&quot;, theadID: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(<span class="number">500</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// atomic&lt;int&gt; == atomic_int</span></span><br><span class="line">    atomic_int m_value = <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Counter c;</span><br><span class="line">    <span class="keyword">auto</span> increment = <span class="built_in">bind</span>(&amp;Counter::increment, &amp;c);</span><br><span class="line">    <span class="keyword">auto</span> decrement = <span class="built_in">bind</span>(&amp;Counter::decrement, &amp;c);</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(increment)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(decrement)</span></span>;</span><br><span class="line"></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过代码的对比可以看出，使用了原子变量之后，就不需要再定义互斥量了，在使用上更加简便，并且这两种方式都能保证在多线程操作过程中数据的正确性，不会出现数据的混乱。</p>
<p>原子类型 atomic<T> 可以封装原始数据最终得到一个原子变量对象，操作原子对象能够得到和操作原始数据一样的效果，当然也可以通过 store() 和 load() 来读写原子对象内部的原始数据。</p>
<h3 id="18-9-线程异步"><a href="#18-9-线程异步" class="headerlink" title="18.9* 线程异步"></a>18.9* 线程异步</h3> 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          Donate
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>Copyright： </strong>
          
          Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2023/03/30/%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
    </footer>
  </div>

   
  <nav class="article-nav">
    
    
      <a href="/2023/03/30/hello-world/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Hello World</div>
      </a>
    
  </nav>

  
   
  
   
    <script src="https://cdn.staticfile.org/twikoo/1.4.18/twikoo.all.min.js"></script>
    <div id="twikoo" class="twikoo"></div>
    <script>
        twikoo.init({
            envId: ""
        })
    </script>
 
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2023
        <i class="ri-heart-fill heart_icon"></i> sharp
      </li>
    </ul>
    <ul>
      <li>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>Visitors:<span id="busuanzi_value_site_uv"></span></span>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>Views:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>    
    </main>
    <div class="float_btns">
      <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

    </div>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="李三水的学习笔记"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E6%97%85%E8%A1%8C/">旅行</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" target="_blank" rel="noopener" href="http://shenyu-vip.lofter.com">摄影</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-3.6.0.min.js"></script>
 
<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->
 
<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: ".tocbot",
    contentSelector: ".article-entry",
    headingSelector: "h1, h2, h3, h4, h5, h6",
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: "main",
    positionFixedSelector: ".tocbot",
    positionFixedClass: "is-position-fixed",
    fixedSidebarOffset: "auto",
  });
</script>

<script src="https://cdn.staticfile.org/jquery-modal/0.9.2/jquery.modal.min.js"></script>
<link
  rel="stylesheet"
  href="https://cdn.staticfile.org/jquery-modal/0.9.2/jquery.modal.min.css"
/>
<script src="https://cdn.staticfile.org/justifiedGallery/3.8.1/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->
 <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.staticfile.org/photoswipe/4.1.3/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.staticfile.org/photoswipe/4.1.3/default-skin/default-skin.min.css">
<script src="https://cdn.staticfile.org/photoswipe/4.1.3/photoswipe.min.js"></script>
<script src="https://cdn.staticfile.org/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script> 
<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->
 
<script src="/js/busuanzi-2.3.pure.min.js"></script>
 
<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->
 
<link rel="stylesheet" href="/css/clipboard.css">
 <script src="https://cdn.staticfile.org/clipboard.js/2.0.10/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>
 
<!-- CanvasBackground -->

<script>
  if (window.mermaid) {
    mermaid.initialize({ theme: "forest" });
  }
</script>


    
    

  </div>
</body>

</html>